[IMP] website: remove meta_data from params of filter, and design card
Call _get_filter_meta_data when needed instead to compute it only once and give it from function to function, it is really trivial to compute and the api of functions are simple from this way. Avoid to use h4/h5 taf for styling bug use bootstrap feature for it. The semantic of <div class='h4'/> vs <h4/> is not the same for SEO Don't make call_to_action_url required + small typo task-2446024 https://github.com/odoo/odoo/pull/65176
This commit is contained in:
@@ -728,18 +728,18 @@
|
||||
<t t-set="fieldsImg" t-value="list(record['image_fields'].values())"/>
|
||||
<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>
|
||||
<div t-if="is_sample" class="h5 o_ribbon_right bg-primary text-uppercase">Sample</div>
|
||||
<img t-if="len(fieldsImg) > 0" class="card-img-top p-3" loading="lazy" t-att-src="fieldsImg[0]"/>
|
||||
<div class="card-body">
|
||||
<h5 t-if="len(fields) > 0" class="card-title">
|
||||
<div t-if="len(fields) > 0" class="card-title h5">
|
||||
<t t-raw="fields[0]"/>
|
||||
</h5>
|
||||
</div>
|
||||
<div t-if="len(fields) > 1" class="card-text">
|
||||
<t t-raw="fields[1]"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-footer d-flex align-items-center">
|
||||
<div t-if="len(fields) > 2" class="card-text">
|
||||
<div t-if="len(fields) > 2" class="card-text text-center">
|
||||
<t t-raw="fields[2]"/>
|
||||
</div>
|
||||
<a t-if="call_to_action" class="btn btn-primary ml-auto" t-att-href="call_to_action_url">
|
||||
@@ -753,12 +753,11 @@
|
||||
<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())"/>
|
||||
<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">
|
||||
<div class="card h-100" t-att-data-url="record['call_to_action_url']">
|
||||
<div t-if="is_sample" class="h5 o_ribbon_right bg-primary text-uppercase">Sample</div>
|
||||
<div t-if="len(fields) > 0" class="card-header h5">
|
||||
<t t-raw="fields[0]"/>
|
||||
</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<img t-if="len(fieldsImg) > 0" class="card-img-top p-3" loading="lazy" t-att-src="fieldsImg[0]"/>
|
||||
<div t-if="len(fields) > 1" class="card-text">
|
||||
@@ -766,7 +765,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-footer d-flex align-items-center">
|
||||
<div t-if="len(fields) > 2" class="card-text">
|
||||
<div t-if="len(fields) > 2" class="card-text w-100 text-center">
|
||||
<t t-raw="fields[2]"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -60,23 +60,23 @@ class WebsiteSnippetFilter(models.Model):
|
||||
if self.website_id and self.env['website'].get_current_website() != self.website_id:
|
||||
return ''
|
||||
|
||||
meta_data = self._get_filter_meta_data()
|
||||
records = self._prepare_values(meta_data, limit, search_domain)
|
||||
records = self._prepare_values(limit, search_domain)
|
||||
is_sample = with_sample and not records
|
||||
if is_sample:
|
||||
records = self._prepare_sample(meta_data)
|
||||
records = self._prepare_sample()
|
||||
View = self.env['ir.ui.view'].sudo().with_context(inherit_branding=False)
|
||||
escaped_records = self._record_to_escaped_values(meta_data, records)
|
||||
escaped_records = self._record_to_escaped_values(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)
|
||||
to_generic=self._to_generic,
|
||||
)).decode('utf-8')
|
||||
return [ET.tostring(el) for el in ET.fromstring('<root>%s</root>' % content).getchildren()]
|
||||
|
||||
def _prepare_values(self, meta_data, limit=None, search_domain=None):
|
||||
def _prepare_values(self, 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
|
||||
if self.filter_id:
|
||||
filter_sudo = self.filter_id.sudo()
|
||||
@@ -91,7 +91,7 @@ class WebsiteSnippetFilter(models.Model):
|
||||
order=','.join(literal_eval(filter_sudo.sort)) or None,
|
||||
limit=limit
|
||||
)
|
||||
return self._filter_records_to_values(meta_data, records)
|
||||
return self._filter_records_to_values(records)
|
||||
except MissingError:
|
||||
_logger.warning("The provided domain %s in 'ir.filters' generated a MissingError in '%s'", domain, self._name)
|
||||
return []
|
||||
@@ -101,13 +101,12 @@ class WebsiteSnippetFilter(models.Model):
|
||||
dynamic_filter=self,
|
||||
limit=limit,
|
||||
search_domain=search_domain,
|
||||
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 _to_generic(self, meta_data, records):
|
||||
def _to_generic(self, records):
|
||||
"""Converts a list of dictionary records into the structure used by generic templates for rendering:
|
||||
|
||||
[{
|
||||
@@ -123,32 +122,36 @@ class WebsiteSnippetFilter(models.Model):
|
||||
'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 for generic templates
|
||||
"""
|
||||
meta_data = self._get_filter_meta_data()
|
||||
|
||||
result = []
|
||||
for record in records:
|
||||
data = self._get_rendering_data_structure()
|
||||
data = {
|
||||
'fields': OrderedDict({}),
|
||||
'image_fields': OrderedDict({}),
|
||||
}
|
||||
for field_name, field_widget in meta_data.items():
|
||||
value = record[field_name]
|
||||
if field_widget in ('binary', 'image'):
|
||||
data['image_fields'][field_name] = value
|
||||
else:
|
||||
data['fields'][field_name] = value
|
||||
data['call_to_action_url'] = record['call_to_action_url']
|
||||
data['call_to_action_url'] = record.get('call_to_action_url')
|
||||
result.append(data)
|
||||
return result
|
||||
|
||||
def _record_to_escaped_values(self, meta_data, records):
|
||||
def _record_to_escaped_values(self, 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
|
||||
"""
|
||||
meta_data = self._get_filter_meta_data()
|
||||
result = []
|
||||
for record in records:
|
||||
data = {}
|
||||
@@ -171,7 +174,7 @@ class WebsiteSnippetFilter(models.Model):
|
||||
value, options)
|
||||
else:
|
||||
data[field_name] = self.escape_falsy_as_empty(value)
|
||||
data['call_to_action_url'] = record['call_to_action_url']
|
||||
data['call_to_action_url'] = record.get('call_to_action_url')
|
||||
result.append(data)
|
||||
return result
|
||||
|
||||
@@ -185,15 +188,16 @@ class WebsiteSnippetFilter(models.Model):
|
||||
@return Tuple containing the field name and the field type
|
||||
"""
|
||||
field_name, _, field_widget = field_name.partition(":")
|
||||
field = model._fields.get(field_name) if model else None
|
||||
if field:
|
||||
field_type = field.type
|
||||
elif 'image' in field_name:
|
||||
field_type = 'image'
|
||||
elif 'price' in field_name:
|
||||
field_type = 'monetary'
|
||||
else:
|
||||
field_type = 'text'
|
||||
if not field_widget:
|
||||
field = model._fields.get(field_name) if model else None
|
||||
if field:
|
||||
field_type = field.type
|
||||
elif 'image' in field_name:
|
||||
field_type = 'image'
|
||||
elif 'price' in field_name:
|
||||
field_type = 'monetary'
|
||||
else:
|
||||
field_type = 'text'
|
||||
return field_name, field_widget or field_type
|
||||
|
||||
def _get_filter_meta_data(self):
|
||||
@@ -210,35 +214,35 @@ class WebsiteSnippetFilter(models.Model):
|
||||
meta_data[field_name] = field_widget
|
||||
return meta_data
|
||||
|
||||
def _prepare_sample(self, meta_data, length=4):
|
||||
def _prepare_sample(self, 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
|
||||
"""
|
||||
if not length:
|
||||
return []
|
||||
|
||||
sample = []
|
||||
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(meta_data, single_sample_data, index)
|
||||
self._fill_sample(single_sample_data, index)
|
||||
sample.append(single_sample_data)
|
||||
return sample
|
||||
|
||||
def _fill_sample(self, meta_data, sample, index):
|
||||
def _fill_sample(self, sample, index):
|
||||
"""
|
||||
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 index: Index of the sample within the dataset
|
||||
"""
|
||||
meta_data = self._get_filter_meta_data()
|
||||
for field_name, field_widget in meta_data.items():
|
||||
if field_name not in sample:
|
||||
if field_widget == 'binary':
|
||||
@@ -252,6 +256,7 @@ class WebsiteSnippetFilter(models.Model):
|
||||
else:
|
||||
sample[field_name] = _('Sample %s', index + 1)
|
||||
sample['call_to_action_url'] = ''
|
||||
return sample
|
||||
|
||||
def _get_hardcoded_sample(self, model):
|
||||
"""
|
||||
@@ -263,24 +268,18 @@ class WebsiteSnippetFilter(models.Model):
|
||||
"""
|
||||
return [{}]
|
||||
|
||||
@api.model
|
||||
def _get_rendering_data_structure(self):
|
||||
return {
|
||||
'fields': OrderedDict({}),
|
||||
'image_fields': OrderedDict({}),
|
||||
}
|
||||
|
||||
def _filter_records_to_values(self, meta_data, records):
|
||||
def _filter_records_to_values(self, records):
|
||||
"""
|
||||
Extract the fields from the data source and put them into a dictionary of values
|
||||
Extract the fields from the data source 'records' and put them into a dictionary of values
|
||||
|
||||
@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()
|
||||
meta_data = self._get_filter_meta_data()
|
||||
|
||||
values = []
|
||||
model = self.env[self.filter_id.model_id]
|
||||
Website = self.env['website']
|
||||
|
||||
@@ -59,9 +59,9 @@ 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')
|
||||
meta_data = dynamic_filter._get_filter_meta_data()
|
||||
|
||||
domain = [('website_published', '=', True)] + website.website_domain() + (search_domain or [])
|
||||
products = ProductProduct.search(domain, limit=limit)
|
||||
|
||||
Reference in New Issue
Block a user