Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion addons/portal_rating/views/rating_templates.xml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
<t t-foreach="range(0, empty_star)" t-as="num">
<i class="fa fa-star-o" role="img"></i>
</t>
<small class="text-muted ms-1">
<small t-if="not hide_rating_count" class="text-muted ms-1">
(<t t-out="rating_count"/>)
</small>
</t>
Expand Down
1 change: 1 addition & 0 deletions addons/test_website/__manifest__.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
],
'web.assets_frontend': [
'test_website/static/src/interactions/**/*',
'test_website/static/src/snippets/**/*.xml',
],
'website.website_builder_assets': [
'test_website/static/src/website_builder/**/*',
Expand Down
2 changes: 2 additions & 0 deletions addons/test_website/models/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ def _search_get_detail(self, website, order, options):
},
'icon': 'fa-check-square-o',
'order': 'name asc, id desc',
'template': 'test_website.search_result_item_test_model',
'group_name': self.env._("Test Models"),
}

def _get_search_highlight_handlers(self):
Expand Down
15 changes: 15 additions & 0 deletions addons/test_website/static/src/snippets/s_searchbar/000.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>

<templates xml:space="preserve">

<t t-name="test_website.search_result_item_test_model">
<t t-foreach="bucket.data" t-as="result" t-key="result_index">
<a t-att-href="result['website_url']" class="h-100 p-2 o_search_result_item">
<div class="o_search_result_item_content">
<div class="fs-6 fw-bold mb-0" t-out="result['name']"/>
</div>
</a>
</t>
</t>

</templates>
8 changes: 2 additions & 6 deletions addons/test_website/tests/test_fuzzy.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,7 @@ def _autocomplete(self, term, expected_count, expected_fuzzy_term, search_type="

def _autocomplete_page(self, term, expected_count, expected_fuzzy_term):
self._autocomplete(term, expected_count, expected_fuzzy_term, search_type="pages", options={
'displayDescription': False, 'displayDetail': False,
'displayExtraDetail': False, 'displayExtraLink': False,
'displayImage': False, 'allowFuzzy': True
'allowFuzzy': True
})

def test_01_many_records(self):
Expand Down Expand Up @@ -98,9 +96,7 @@ def test_02_pages_search(self):
# ORM level, not in SQL, due to how `inherits` works.
self.env['website'].browse(1)._search_with_fuzzy(
'pages', 'test', limit=5, order='name asc, website_id desc, id', options={
'displayDescription': False, 'displayDetail': False,
'displayExtraDetail': False, 'displayExtraLink': False,
'displayImage': False, 'allowFuzzy': True
'allowFuzzy': True
}
)

Expand Down
117 changes: 52 additions & 65 deletions addons/website/controllers/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -587,7 +587,7 @@ def _get_search_order(self, order):
return 'is_published desc, %s, id desc' % order

@http.route('/website/snippet/autocomplete', type='jsonrpc', auth='public', website=True, readonly=True)
def autocomplete(self, search_type=None, term=None, order=None, limit=5, max_nb_chars=999, options=None):
def autocomplete(self, search_type=None, term=None, order=None, limit=6, max_nb_chars=999, options=None):
"""
Returns list of results according to the term and options

Expand All @@ -612,54 +612,64 @@ def autocomplete(self, search_type=None, term=None, order=None, limit=5, max_nb_
order = self._get_search_order(order)
options = options or {}
results_count, search_results, fuzzy_term = request.website._search_with_fuzzy(search_type, term, limit, order, options)
# Sort result based in sequence for ordered results.
search_results.sort(key=lambda d: d.get('sequence', float('inf')))
if not results_count:
return {
'results': [],
'results': {},
'results_count': 0,
'parts': {},
}
term = fuzzy_term or term
search_results = request.website._search_render_results(search_results, limit)
search_results = request.website.sudo()._search_render_results(search_results, limit)

mappings = []
results_data = []
result = {}
for search_result in search_results:
for result in search_result['results_data']:
result['model'] = search_result['model']
results_data.append(result)
if not len(search_result['results_data']):
continue
search_result['results_data'].sort(key=lambda r: r.get('name', ''), reverse='name desc' in order)
mappings.append(search_result['mapping'])
if search_type == 'all':
# Only supported order for 'all' is on name
results_data.sort(key=lambda r: r.get('name', ''), reverse='name desc' in order)
results_data = results_data[:limit]
result = []
for record in results_data:
mapping = record['_mapping']
model = request.env[record['model']]
mapped = {
'_fa': record.get('_fa'),
}
for mapped_name, field_meta in mapping.items():
value = record.get(field_meta.get('name'))
if not value:
mapped[mapped_name] = ''
continue
field_type = field_meta.get('type')
if field_type == 'text' and field_meta.get('truncate', True):
value = self._shorten_around_match(value, term, max_nb_chars)

if field_meta.get('match'):
skip_field, value, field_type = model._search_highlight_field(field_meta, value, term)
if skip_field:
group_name = search_result.get("group_name")
group_key = '_'.join(group_name.lower().split())
result_data = []
for record in search_result['results_data']:
model = request.env[search_result['model']]
mapping = record['_mapping']
mapped = {
'_fa': record.get('_fa'),
}
model = request.env[search_result['model']]
for mapped_name, field_meta in mapping.items():
value = record.get(field_meta.get('name'))
if not value:
mapped[mapped_name] = ''
continue

if field_type not in ('image', 'binary') and ('ir.qweb.field.%s' % field_type) in request.env:
opt = {}
if field_type == 'monetary':
opt['display_currency'] = options['display_currency']
value = request.env[('ir.qweb.field.%s' % field_type)].value_to_html(value, opt)
mapped[mapped_name] = escape(value)
result.append(mapped)
field_type = field_meta.get('type')
if field_type == 'text' and field_meta.get('truncate', True):
value = self._shorten_around_match(value, term, max_nb_chars)

if field_meta.get('match'):
skip_field, value, field_type = model._search_highlight_field(field_meta, value, term)
if skip_field:
continue

if field_type not in ('image', 'binary') and ('ir.qweb.field.%s' % field_type) in request.env:
opt = {}
if field_type == 'monetary':
opt['display_currency'] = options['display_currency']
elif field_type == 'float':
opt['precision'] = field_meta.get('precision', 2)
value = request.env[('ir.qweb.field.%s' % field_type)].value_to_html(value, opt)
mapped[mapped_name] = escape(value)
result_data.append(mapped)

result[group_key] = {
"groupName": group_name,
"templateKey": search_result.get("template_key"),
"search_count": search_result.get('count'),
"data": result_data,
}

return {
'results': result,
Expand All @@ -670,11 +680,6 @@ def autocomplete(self, search_type=None, term=None, order=None, limit=5, max_nb_

def _get_page_search_options(self, **post):
return {
'displayDescription': False,
'displayDetail': False,
'displayExtraDetail': False,
'displayExtraLink': False,
'displayImage': False,
'allowFuzzy': not post.get('noFuzzy'),
}

Expand Down Expand Up @@ -708,44 +713,26 @@ def pages_list(self, page=1, search='', **kw):

def _get_hybrid_search_options(self, **post):
return {
'displayDescription': True,
'displayDetail': True,
'displayExtraDetail': True,
'displayExtraLink': True,
'displayImage': True,
'allowFuzzy': not post.get('noFuzzy'),
}

@http.route([
'/website/search',
'/website/search/page/<int:page>',
'/website/search/<string:search_type>',
'/website/search/<string:search_type>/page/<int:page>',
], type='http', auth="public", website=True, sitemap=False, readonly=True)
def hybrid_list(self, page=1, search='', search_type='all', **kw):
def hybrid_list(self, search='', search_type='all', **kw):
if not search:
return request.render("website.list_hybrid")

options = self._get_hybrid_search_options(**kw)
data = self.autocomplete(search_type=search_type, term=search, order='name asc', limit=500, max_nb_chars=200, options=options)
data = self.autocomplete(search_type=search_type, term=search, order='name asc', limit=100, max_nb_chars=200, options=options)

results = data.get('results', [])
search_count = len(results)
search_count = data.get('results_count', 0)
parts = data.get('parts', {})

step = 50
pager = portal_pager(
url="/website/search/%s" % search_type,
url_args={'search': search},
total=search_count,
page=page,
step=step
)

results = results[(page - 1) * step:page * step]

# TODO: Implement pagination or load more button for more records
values = {
'pager': pager,
'results': results,
'parts': parts,
'search': search,
Expand Down
12 changes: 11 additions & 1 deletion addons/website/models/mixins.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,15 @@ def _get_background(self, height=None, width=None):
img = img[:-1] + suffix + ')'
return img

def _get_image_url(self):
self.ensure_one()
properties = json_safe.loads(self.cover_properties)
img = properties.get('background-image', None)
if not img:
return None
match = re.search(r"url\(\s*(['\"]?)(.*?)\1\s*\)", img)
return match.group(2) if match else img

def write(self, vals):
if 'cover_properties' not in vals:
return super().write(vals)
Expand Down Expand Up @@ -694,7 +703,8 @@ def _search_fetch(self, search_detail, search, limit, order):
return results, count

def _search_render_results(self, fetch_fields, mapping, icon, limit):
results_data = self.read(fetch_fields)[:limit]
# Some fields are not avaiable in public user group - require sudo to complete result
results_data = self.sudo().read(fetch_fields)[:limit]
for result in results_data:
result['_fa'] = icon
result['_mapping'] = mapping
Expand Down
13 changes: 6 additions & 7 deletions addons/website/models/website_page.py
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,6 @@ def get_website_meta(self):

@api.model
def _search_get_detail(self, website, order, options):
with_description = options['displayDescription']
# Read access on website.page requires sudo.
requires_sudo = True
domain = [website.website_domain()]
Expand All @@ -224,16 +223,13 @@ def _search_get_detail(self, website, order, options):
[('group_ids', '=', False)], [('group_ids', 'in', self.env.user.group_ids.ids)]
]))

search_fields = ['name', 'url']
fetch_fields = ['id', 'name', 'url']
search_fields = ['name', 'url', 'arch_db']
fetch_fields = ['id', 'name', 'url', 'arch']
mapping = {
'name': {'name': 'name', 'type': 'text', 'match': True},
'website_url': {'name': 'url', 'type': 'text', 'truncate': False},
'description': {'name': 'arch', 'type': 'text', 'html': True, 'match': True}
}
if with_description:
search_fields.append('arch_db')
fetch_fields.append('arch')
mapping['description'] = {'name': 'arch', 'type': 'text', 'html': True, 'match': True}
return {
'model': 'website.page',
'base_domain': domain,
Expand All @@ -242,6 +238,9 @@ def _search_get_detail(self, website, order, options):
'fetch_fields': fetch_fields,
'mapping': mapping,
'icon': 'fa-file-o',
'template_key': 'website.search_items_page',
'group_name': self.env._("Pages"),
'sequence': 10,
}

@api.model
Expand Down
3 changes: 1 addition & 2 deletions addons/website/static/src/@types/plugins.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ declare module "plugins" {
import { MegaMenuOptionShared } from "@website/builder/plugins/options/mega_menu_option_plugin";
import { NavTabsStyleOptionShared } from "@website/builder/plugins/options/navtabs_style_option_plugin";
import { WebsiteParallaxShared } from "@website/builder/plugins/options/parallax_option_plugin";
import { searchbar_option_display_items, searchbar_option_order_by_items } from "@website/builder/plugins/options/searchbar_option_plugin";
import { searchbar_option_order_by_items } from "@website/builder/plugins/options/searchbar_option_plugin";
import { SocialMediaOptionShared } from "@website/builder/plugins/options/social_media_option_plugin";
import { visibility_selector_parameters } from "@website/builder/plugins/options/visibility_option_plugin";
import { WebsitePageConfigOptionShared } from "@website/builder/plugins/options/website_page_config_option_plugin";
Expand Down Expand Up @@ -92,7 +92,6 @@ declare module "plugins" {
footer_templates_providers: footer_templates_providers;

// Data
searchbar_option_display_items: searchbar_option_display_items;
searchbar_option_order_by_items: searchbar_option_order_by_items;
theme_options: theme_options;
visibility_selector_parameters: visibility_selector_parameters;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,5 @@ export class SearchbarOption extends BaseOptionComponent {
this.getItemValue = useGetItemValue();

this.orderByItems = this.getResource("searchbar_option_order_by_items");
this.displayItems = this.getResource("searchbar_option_display_items");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,6 @@
<span class="flex-grow-1 w-100">results</span>
</BuilderRow>

<t t-if="this.getItemValue('limit_opt') > 0" t-foreach="this.displayItems" t-as="item" t-key="`${item.label}_${item.dataAttribute}_${item.dependency}`">
<BuilderRow label="item.label" t-if="this.isActiveItem(item.dependency)">
<BuilderCheckbox action="'setNonEmptyDataAttribute'" actionParam="item.dataAttribute" actionValue="'true'"/>
</BuilderRow>
</t>
<BuilderRow label.translate="Style">
<BuilderSelect action="'setSearchbarStyle'">
<BuilderSelectItem actionParam="'default'">Default Input Style</BuilderSelectItem>
Expand Down
Loading