diff --git a/addons/website/controllers/main.py b/addons/website/controllers/main.py index fc6aac5c059..49989bbe903 100644 --- a/addons/website/controllers/main.py +++ b/addons/website/controllers/main.py @@ -551,7 +551,7 @@ class Website(Home): pages = Page.search(domain, order=sort_order) if sortby != 'url' or not request.session.debug: - pages = pages.filtered(pages._is_most_specific_page) + pages = pages._get_most_specific_pages() pages_count = len(pages) step = 50 diff --git a/addons/website/models/mixins.py b/addons/website/models/mixins.py index 039196562c2..240dfdfad17 100644 --- a/addons/website/models/mixins.py +++ b/addons/website/models/mixins.py @@ -2,11 +2,14 @@ # Part of Odoo. See LICENSE file for full copyright and licensing details. import logging +import re from odoo import api, fields, models, _ +from odoo.addons.website.tools import text_from_html from odoo.http import request from odoo.osv import expression from odoo.exceptions import AccessError +from odoo.tools import escape_psql from odoo.tools.json import scriptsafe as json_safe logger = logging.getLogger(__name__) @@ -252,3 +255,85 @@ class WebsitePublishedMultiMixin(WebsitePublishedMixin): return (['!'] if value is False else []) + expression.AND([is_published, on_current_website]) else: # should be in the backend, return things that are published anywhere return is_published + + +class WebsiteSearchableMixin(models.AbstractModel): + """Mixin to be inherited by all models that need to searchable through website""" + _name = 'website.searchable.mixin' + _description = 'Website Searchable Mixin' + + @api.model + def _search_build_domain(self, domain_list, search, fields, extra=None): + """ + Builds a search domain AND-combining a base domain with partial matches of each term in + the search expression in any of the fields. + + :param domain_list: base domain list combined in the search expression + :param search: search expression string + :param fields: list of field names to match the terms of the search expression with + :param extra: function that returns an additional subdomain for a search term + + :return: domain limited to the matches of the search expression + """ + domains = domain_list.copy() + if search: + for search_term in search.split(' '): + subdomains = [[(field, 'ilike', escape_psql(search_term))] for field in fields] + if extra: + subdomains.append(extra(self.env, search_term)) + domains.append(expression.OR(subdomains)) + return expression.AND(domains) + + @api.model + def _search_get_detail(self, website, order, options): + """ + Returns indications on how to perform the searches + + :param website: website within which the search is done + :param order: order in which the results are to be returned + :param options: search options + + :return: search detail as expected in elements of the result of website._search_get_details() + These elements contain the following fields: + - model: name of the searched model + - base_domain: list of domains within which to perform the search + - search_fields: fields within which the search term must be found + - fetch_fields: fields from which data must be fetched + - mapping: mapping from the results towards the structure used in rendering templates. + The mapping is a dict that associates the rendering name of each field + to a dict containing the 'name' of the field in the results list and the 'type' + that must be used for rendering the value + - icon: name of the icon to use if there is no image + + This method must be implemented by all models that inherit this mixin. + """ + raise NotImplementedError() + + @api.model + def _search_fetch(self, search_detail, search, limit, order): + fields = search_detail['search_fields'] + base_domain = search_detail['base_domain'] + domain = self._search_build_domain(base_domain, search, fields, search_detail.get('search_extra')) + model = self.sudo() if search_detail.get('requires_sudo') else self + results = model.search( + domain, + limit=limit, + order=search_detail.get('order', order) + ) + count = model.search_count(domain) + return results, count + + def _search_render_results(self, fetch_fields, mapping, icon, limit): + results_data = self.read(fetch_fields)[:limit] + for result in results_data: + result['_fa'] = icon + result['_mapping'] = mapping + html_fields = [config['name'] for config in mapping.values() if config.get('html')] + if html_fields: + for result, data in zip(self, results_data): + for html_field in html_fields: + if data[html_field]: + text = text_from_html(data[html_field]) + text = re.sub('\\s+', ' ', text).strip() + data[html_field] = text + return results_data diff --git a/addons/website/models/website.py b/addons/website/models/website.py index 4286a341937..48344b90e44 100644 --- a/addons/website/models/website.py +++ b/addons/website/models/website.py @@ -1483,19 +1483,7 @@ class Website(models.Model): :param order: order in which the results are to be returned :param options: search options - :return list of objects with: - - model: name of the searched model - - base_domain: domain within which to perform the search - - search_fields: fields within which the search term must be found - - secondary_search_fields: optionally performs a second search - - filter_function: optionally filters obtained results - - fetch_fields: fields from which data must be fetched - - patch_data_function: optionally sets additional fields for rendering - - mapping: mapping from the results towards the structure used in rendering templates. - The mapping is a dict that associates the rendering name of each field - to a dict containing the 'name' of the field in the results list and the 'type' - that must be used for rendering the value - - icon: name of the icon to use if there is no image + :return: list of search details obtained from the `website.searchable.mixin`'s `_search_get_detail()` """ result = [] if search_type in ['pages', 'all']: @@ -1554,36 +1542,11 @@ class Website(models.Model): total_count = 0 for search_detail in search_details: model = self.env[search_detail['model']] - fields = search_detail['search_fields'] - base_domain = search_detail['base_domain'] - domain = self._search_build_domain(base_domain, search, fields, search_detail.get('search_extra')) - if search_detail.get('requires_sudo'): - model = model.sudo() - results = model.search( - domain, - limit=limit, - order=search_detail.get('order', order) - ) - if search: - base_results = results - secondary_fields = search_detail.get('secondary_search_fields') - if secondary_fields: - domain = self._search_build_domain(base_domain, search, secondary_fields) - results = model.search( - domain, - limit=limit, - order=search_detail.get('order', order) - ) - results = results.union(base_results) - filter_function = search_detail.get('filter_function') - if filter_function: - results = results.filtered(lambda result: filter_function(search, result, base_results, results)) + results, count = model._search_fetch(search_detail, search, limit, order) search_detail['results'] = results - count = model.search_count(domain) total_count += count search_detail['count'] = count all_results.append(search_detail) - return total_count, all_results def _search_render_results(self, search_details, limit): @@ -1599,23 +1562,9 @@ class Website(models.Model): for search_detail in search_details: fields = search_detail['fetch_fields'] results = search_detail['results'] - results_data = results.read(fields)[:limit] icon = search_detail['icon'] mapping = search_detail['mapping'] - for result in results_data: - result['_fa'] = icon - result['_mapping'] = mapping - html_fields = [config['name'] for config in mapping.values() if config.get('html')] - patch_function = search_detail.get('patch_data_function') - if patch_function or html_fields: - for result, data in zip(results, results_data): - for html_field in html_fields: - if data[html_field]: - text = self._search_text_from_html(data[html_field]) - text = re.sub('\\s+', ' ', text).strip() - data[html_field] = text - if patch_function: - patch_function(result, data) + results_data = results._search_render_results(fields, mapping, icon, limit) search_detail['results_data'] = results_data return search_details diff --git a/addons/website/models/website_page.py b/addons/website/models/website_page.py index 8147da63357..3407a84542a 100644 --- a/addons/website/models/website_page.py +++ b/addons/website/models/website_page.py @@ -4,6 +4,7 @@ import re from odoo.addons.http_routing.models.ir_http import slugify +from odoo.addons.website.tools import text_from_html from odoo import api, fields, models from odoo.tools.safe_eval import safe_eval @@ -11,7 +12,10 @@ from odoo.tools.safe_eval import safe_eval class Page(models.Model): _name = 'website.page' _inherits = {'ir.ui.view': 'view_id'} - _inherit = 'website.published.multi.mixin' + _inherit = [ + 'website.published.multi.mixin', + 'website.searchable.mixin', + ] _description = 'Page' _order = 'website_id' @@ -57,14 +61,16 @@ class Page(models.Model): not page.date_publish or page.date_publish < fields.Datetime.now() ) - def _is_most_specific_page(self, page_to_test): - '''This will test if page_to_test is the most specific page in self.''' - pages_for_url = self.sorted(key=lambda p: not p.website_id).filtered(lambda page: page.url == page_to_test.url) - - # this works because pages are _order'ed by website_id - most_specific_page = pages_for_url[0] - - return most_specific_page == page_to_test + def _get_most_specific_pages(self): + ''' Returns the most specific pages in self. ''' + ids = [] + previous_page = None + # Iterate a single time on the whole list sorted on specific-website first. + for page in self.sorted(key=lambda p: (p.url, not p.website_id)): + if not previous_page or page.url != previous_page.url: + ids.append(page.id) + previous_page = page + return self.filtered(lambda page: page.id in ids) def get_page_properties(self): self.ensure_one() @@ -233,15 +239,6 @@ class Page(models.Model): @api.model def _search_get_detail(self, website, order, options): - """ - Returns indications on how to perform the searches - - :param website: website within which the search is done - :param order: order in which the results are to be returned - :param options: search options - - :return search detail as expected in elements of the result of website._search_get_details() - """ with_description = options['displayDescription'] # Read access on website.page requires sudo. requires_sudo = True @@ -250,36 +247,42 @@ class Page(models.Model): # Rule must be reinforced because of sudo. domain.append([('website_published', '=', True)]) + search_fields = ['name', 'url'] fetch_fields = ['id', 'name', 'url'] mapping = { 'name': {'name': 'name', 'type': 'text', 'match': True}, 'website_url': {'name': 'url', 'type': 'text'}, } if with_description: + search_fields.append('view_id.arch_db') fetch_fields.append('arch') mapping['description'] = {'name': 'arch', 'type': 'text', 'html': True, 'match': True} - def filter_page(search, page, base_pages, all_pages): - if not all_pages._is_most_specific_page(page): - return False - if page in base_pages: - return True - text = '%s %s' % (page.name, page.url) - if with_description: - text = '%s %s' % (text, website._search_text_from_html(page.arch)) - pattern = '|'.join([re.escape(search_term) for search_term in search.split()]) - return re.findall('(%s)' % pattern, text, flags=re.I) if pattern else False return { 'model': 'website.page', 'base_domain': domain, 'requires_sudo': requires_sudo, - 'search_fields': ['name', 'url'], - 'secondary_search_fields': ['name', 'url', 'view_id.arch_db'] if with_description else None, - 'filter_function': filter_page, + 'search_fields': search_fields, 'fetch_fields': fetch_fields, 'mapping': mapping, 'icon': 'fa-file-o', } + @api.model + def _search_fetch(self, search_detail, search, limit, order): + with_description = 'description' in search_detail['mapping'] + results, count = super()._search_fetch(search_detail, search, limit, order) + def filter_page(search, page, all_pages): + # Search might have matched words in the xml tags and parameters therefore we make + # sure the terms actually appear inside the text. + text = '%s %s %s' % (page.name, page.url, text_from_html(page.arch)) + pattern = '|'.join([re.escape(search_term) for search_term in search.split()]) + return re.findall('(%s)' % pattern, text, flags=re.I) if pattern else False + if 'url' not in order: + results = results._get_most_specific_pages() + if search and with_description: + results = results.filtered(lambda result: filter_page(search, result, results)) + return results, count + # this is just a dummy function to be used as ormcache key def _cached_response(): diff --git a/addons/website_blog/models/website_blog.py b/addons/website_blog/models/website_blog.py index cdd136dcfbb..0f56fe481e1 100644 --- a/addons/website_blog/models/website_blog.py +++ b/addons/website_blog/models/website_blog.py @@ -14,7 +14,13 @@ from odoo.tools import html2plaintext class Blog(models.Model): _name = 'blog.blog' _description = 'Blog' - _inherit = ['mail.thread', 'website.seo.metadata', 'website.multi.mixin', 'website.cover_properties.mixin'] + _inherit = [ + 'mail.thread', + 'website.seo.metadata', + 'website.multi.mixin', + 'website.cover_properties.mixin', + 'website.searchable.mixin', + ] _order = 'name' name = fields.Char('Blog Name', required=True, translate=True) @@ -88,7 +94,6 @@ class Blog(models.Model): @api.model def _search_get_detail(self, website, order, options): - """See website_page._search_get_detail()""" with_description = options['displayDescription'] search_fields = ['name'] fetch_fields = ['id', 'name'] @@ -100,19 +105,21 @@ class Blog(models.Model): search_fields.append('subtitle') fetch_fields.append('subtitle') mapping['description'] = {'name': 'subtitle', 'type': 'text', 'match': True} - def patch_blog(blog, data): - data['url'] = '/blog/%s' % data['id'] return { 'model': 'blog.blog', 'base_domain': [website.website_domain()], 'search_fields': search_fields, 'fetch_fields': fetch_fields, - 'patch_data_function': patch_blog, 'mapping': mapping, 'icon': 'fa-rss-square', 'order': 'name desc, id desc' if 'name desc' in order else 'name asc, id desc', } + def _search_render_results(self, fetch_fields, mapping, icon, limit): + results_data = super()._search_render_results(fetch_fields, mapping, icon, limit) + for data in results_data: + data['url'] = '/blog/%s' % data['id'] + return results_data class BlogTagCategory(models.Model): _name = 'blog.tag.category' @@ -145,7 +152,8 @@ class BlogTag(models.Model): class BlogPost(models.Model): _name = "blog.post" _description = "Blog Post" - _inherit = ['mail.thread', 'website.seo.metadata', 'website.published.multi.mixin', 'website.cover_properties.mixin'] + _inherit = ['mail.thread', 'website.seo.metadata', 'website.published.multi.mixin', + 'website.cover_properties.mixin', 'website.searchable.mixin'] _order = 'id DESC' _mail_post_access = 'read' @@ -297,7 +305,6 @@ class BlogPost(models.Model): @api.model def _search_get_detail(self, website, order, options): - """See website_page._search_get_detail()""" with_description = options['displayDescription'] with_date = options['displayDetail'] blog = options.get('blog') diff --git a/addons/website_event/models/event_event.py b/addons/website_event/models/event_event.py index 91f5110a9cf..e598ec4b124 100644 --- a/addons/website_event/models/event_event.py +++ b/addons/website_event/models/event_event.py @@ -23,7 +23,8 @@ class Event(models.Model): 'event.event', 'website.seo.metadata', 'website.published.multi.mixin', - 'website.cover_properties.mixin' + 'website.cover_properties.mixin', + 'website.searchable.mixin', ] def _default_cover_properties(self): @@ -467,7 +468,6 @@ class Event(models.Model): @api.model def _search_get_detail(self, website, order, options): - """See website_page._search_get_detail()""" with_description = options['displayDescription'] with_date = options['displayDetail'] date = options.get('date', 'all') @@ -527,16 +527,11 @@ class Event(models.Model): mapping['description'] = {'name': 'subtitle', 'type': 'text', 'match': True} if with_date: mapping['detail'] = {'name': 'range', 'type': 'html'} - def patch_event(event, data): - begin = self.env['ir.qweb.field.date'].record_to_html(event, 'date_begin', {}) - end = self.env['ir.qweb.field.date'].record_to_html(event, 'date_end', {}) - data['range'] = '%s🠖%s' % (begin, end) if begin != end else begin return { 'model': 'event.event', 'base_domain': domain, 'search_fields': search_fields, 'fetch_fields': fetch_fields, - 'patch_data_function': patch_event if with_date else None, 'mapping': mapping, 'icon': 'fa-ticket', # for website_event main controller: @@ -546,3 +541,13 @@ class Event(models.Model): 'no_date_domain': no_date_domain, 'no_country_domain': no_country_domain, } + + def _search_render_results(self, fetch_fields, mapping, icon, limit): + with_date = 'detail' in mapping + results_data = super()._search_render_results(fetch_fields, mapping, icon, limit) + if with_date: + for event, data in zip(self, results_data): + begin = self.env['ir.qweb.field.date'].record_to_html(event, 'date_begin', {}) + end = self.env['ir.qweb.field.date'].record_to_html(event, 'date_end', {}) + data['range'] = '%s🠖%s' % (begin, end) if begin != end else begin + return results_data diff --git a/addons/website_forum/models/forum.py b/addons/website_forum/models/forum.py index f8f0fc068b8..f21d5984a4d 100644 --- a/addons/website_forum/models/forum.py +++ b/addons/website_forum/models/forum.py @@ -19,7 +19,13 @@ _logger = logging.getLogger(__name__) class Forum(models.Model): _name = 'forum.forum' _description = 'Forum' - _inherit = ['mail.thread', 'image.mixin', 'website.seo.metadata', 'website.multi.mixin'] + _inherit = [ + 'mail.thread', + 'image.mixin', + 'website.seo.metadata', + 'website.multi.mixin', + 'website.searchable.mixin', + ] _order = "sequence" # description and use @@ -263,7 +269,6 @@ class Forum(models.Model): @api.model def _search_get_detail(self, website, order, options): - """See website_page._search_get_detail()""" with_description = options['displayDescription'] search_fields = ['name'] fetch_fields = ['id', 'name'] @@ -275,25 +280,32 @@ class Forum(models.Model): search_fields.append('description') fetch_fields.append('description') mapping['description'] = {'name': 'description', 'type': 'text', 'match': True} - def patch_forum(forum, data): - data['website_url'] = forum._compute_website_url() return { 'model': 'forum.forum', 'base_domain': [website.website_domain()], 'search_fields': search_fields, 'fetch_fields': fetch_fields, - 'patch_data_function': patch_forum, 'mapping': mapping, 'icon': 'fa-comments-o', 'order': 'name desc, id desc' if 'name desc' in order else 'name asc, id desc', } + def _search_render_results(self, fetch_fields, mapping, icon, limit): + results_data = super()._search_render_results(fetch_fields, mapping, icon, limit) + for forum, data in zip(self, results_data): + data['website_url'] = forum._compute_website_url() + return results_data + class Post(models.Model): _name = 'forum.post' _description = 'Forum Post' - _inherit = ['mail.thread', 'website.seo.metadata'] + _inherit = [ + 'mail.thread', + 'website.seo.metadata', + 'website.searchable.mixin', + ] _order = "is_correct DESC, vote_count DESC, write_date DESC" name = fields.Char('Title') @@ -963,7 +975,6 @@ class Post(models.Model): @api.model def _search_get_detail(self, website, order, options): - """See website_page._search_get_detail()""" with_description = options['displayDescription'] with_date = options['displayDetail'] search_fields = ['name'] @@ -1012,21 +1023,25 @@ class Post(models.Model): if with_date: fetch_fields.append('write_date') mapping['detail'] = {'name': 'date', 'type': 'html'} - def patch_forum_post(post, data): - data['website_url'] = post._compute_website_url() - if with_date: - data['date'] = self.env['ir.qweb.field.date'].record_to_html(post, 'write_date', {}) return { 'model': 'forum.post', 'base_domain': [domain], 'search_fields': search_fields, 'fetch_fields': fetch_fields, - 'patch_data_function': patch_forum_post, 'mapping': mapping, 'icon': 'fa-comment-o', 'order': order, } + def _search_render_results(self, fetch_fields, mapping, icon, limit): + with_date = 'detail' in mapping + results_data = super()._search_render_results(fetch_fields, mapping, icon, limit) + for post, data in zip(self, results_data): + data['website_url'] = post._compute_website_url() + if with_date: + data['date'] = self.env['ir.qweb.field.date'].record_to_html(post, 'write_date', {}) + return results_data + class PostReason(models.Model): _name = "forum.post.reason" diff --git a/addons/website_sale/models/product_misc.py b/addons/website_sale/models/product_misc.py index e8722da33d0..d91b58ffbcf 100644 --- a/addons/website_sale/models/product_misc.py +++ b/addons/website_sale/models/product_misc.py @@ -139,7 +139,12 @@ class ProductPricelist(models.Model): class ProductPublicCategory(models.Model): _name = "product.public.category" - _inherit = ["website.seo.metadata", "website.multi.mixin", 'image.mixin'] + _inherit = [ + 'website.seo.metadata', + 'website.multi.mixin', + 'website.searchable.mixin', + 'image.mixin', + ] _description = "Website Product Category" _parent_store = True _order = "sequence, name, id" @@ -179,7 +184,6 @@ class ProductPublicCategory(models.Model): @api.model def _search_get_detail(self, website, order, options): - """See website_page._search_get_detail()""" with_description = options['displayDescription'] search_fields = ['name'] fetch_fields = ['id', 'name'] @@ -191,15 +195,18 @@ class ProductPublicCategory(models.Model): search_fields.append('website_description') fetch_fields.append('website_description') mapping['description'] = {'name': 'website_description', 'type': 'text', 'match': True} - def patch_category(category, data): - data['url'] = '/shop/category/%s' % data['id'] return { 'model': 'product.public.category', 'base_domain': [], # categories are not website-specific 'search_fields': search_fields, 'fetch_fields': fetch_fields, - 'patch_data_function': patch_category, 'mapping': mapping, 'icon': 'fa-folder-o', 'order': 'name desc, id desc' if 'name desc' in order else 'name asc, id desc', } + + def _search_render_results(self, fetch_fields, mapping, icon, limit): + results_data = super()._search_render_results(fetch_fields, mapping, icon, limit) + for data in results_data: + data['url'] = '/shop/category/%s' % data['id'] + return results_data diff --git a/addons/website_sale/models/product_template.py b/addons/website_sale/models/product_template.py index bf67b0342af..156105ce6a2 100644 --- a/addons/website_sale/models/product_template.py +++ b/addons/website_sale/models/product_template.py @@ -8,7 +8,13 @@ from odoo.osv import expression class ProductTemplate(models.Model): - _inherit = ["product.template", "website.seo.metadata", 'website.published.multi.mixin', 'rating.mixin'] + _inherit = [ + "product.template", + "website.seo.metadata", + 'website.published.multi.mixin', + 'website.searchable.mixin', + 'rating.mixin', + ] _name = 'product.template' _mail_post_access = 'read' _check_company_auto = True @@ -323,7 +329,6 @@ class ProductTemplate(models.Model): @api.model def _search_get_detail(self, website, order, options): - """See website_page._search_get_detail()""" with_image = options['displayImage'] with_description = options['displayDescription'] with_category = options['displayExtraLink'] @@ -367,15 +372,29 @@ class ProductTemplate(models.Model): fetch_fields.append('description_sale') mapping['description'] = {'name': 'description_sale', 'type': 'text', 'match': True} if with_price: - mapping['detail'] = {'name': 'price', 'type': 'html'} - mapping['detail_strike'] = {'name': 'list_price', 'type': 'html'} + mapping['detail'] = {'name': 'price', 'type': 'html', 'display_currency': options['display_currency']} + mapping['detail_strike'] = {'name': 'list_price', 'type': 'html', 'display_currency': options['display_currency']} if with_category: mapping['extra_link'] = {'name': 'category', 'type': 'text', 'match': True} mapping['extra_link_url'] = {'name': 'category_url', 'type': 'text'} - def patch_product(product, data): + return { + 'model': 'product.template', + 'base_domain': domains, + 'search_fields': search_fields, + 'fetch_fields': fetch_fields, + 'mapping': mapping, + 'icon': 'fa-shopping-cart', + } + + def _search_render_results(self, fetch_fields, mapping, icon, limit): + with_image = 'image_url' in mapping + with_category = 'extra_link' in mapping + with_price = 'detail' in mapping + results_data = super()._search_render_results(fetch_fields, mapping, icon, limit) + for product, data in zip(self, results_data): if with_price: combination_info = product._get_combination_info(only_template=True) - monetary_options = {'display_currency': options['display_currency']} + monetary_options = {'display_currency': mapping['detail']['display_currency']} data['price'] = self.env['ir.qweb.field.monetary'].value_to_html(combination_info['price'], monetary_options) if combination_info['has_discounted_price']: data['list_price'] = self.env['ir.qweb.field.monetary'].value_to_html(combination_info['list_price'], monetary_options) @@ -385,12 +404,4 @@ class ProductTemplate(models.Model): data['category'] = _('Category: %s', product.public_categ_ids.name) slugs = [slug(category) for category in product.public_categ_ids] data['category_url'] = '/shop/category/%s' % ','.join(slugs) - return { - 'model': 'product.template', - 'base_domain': domains, - 'search_fields': search_fields, - 'fetch_fields': fetch_fields, - 'patch_data_function': patch_product, - 'mapping': mapping, - 'icon': 'fa-shopping-cart', - } + return results_data diff --git a/addons/website_slides/models/slide_channel.py b/addons/website_slides/models/slide_channel.py index e3e070311e7..ae09193e26c 100644 --- a/addons/website_slides/models/slide_channel.py +++ b/addons/website_slides/models/slide_channel.py @@ -137,7 +137,10 @@ class Channel(models.Model): 'mail.thread', 'rating.mixin', 'mail.activity.mixin', 'image.mixin', - 'website.seo.metadata', 'website.published.multi.mixin'] + 'website.seo.metadata', + 'website.published.multi.mixin', + 'website.searchable.mixin', + ] _order = 'sequence, id' def _default_access_token(self): @@ -834,7 +837,6 @@ class Channel(models.Model): @api.model def _search_get_detail(self, website, order, options): - """See website_page._search_get_detail()""" with_description = options['displayDescription'] with_date = options['displayDetail'] my = options.get('my') diff --git a/addons/website_slides/models/slide_slide.py b/addons/website_slides/models/slide_slide.py index 56613f693d6..627f4c05a8a 100644 --- a/addons/website_slides/models/slide_slide.py +++ b/addons/website_slides/models/slide_slide.py @@ -116,7 +116,10 @@ class Slide(models.Model): _inherit = [ 'mail.thread', 'image.mixin', - 'website.seo.metadata', 'website.published.mixin'] + 'website.seo.metadata', + 'website.published.mixin', + 'website.searchable.mixin', + ] _description = 'Slides' _mail_post_access = 'read' _order_by_strategy = { @@ -890,7 +893,6 @@ class Slide(models.Model): @api.model def _search_get_detail(self, website, order, options): - """See website_page._search_get_detail()""" with_description = options['displayDescription'] search_fields = ['name'] fetch_fields = ['id', 'name'] @@ -904,6 +906,17 @@ class Slide(models.Model): search_fields.append('description') fetch_fields.append('description') mapping['description'] = {'name': 'description', 'type': 'text', 'html': True, 'match': True} + return { + 'model': 'slide.slide', + 'base_domain': [website.website_domain()], + 'search_fields': search_fields, + 'fetch_fields': fetch_fields, + 'mapping': mapping, + 'icon': 'fa-shopping-cart', + 'order': 'name desc, id desc' if 'name desc' in order else 'name asc, id desc', + } + + def _search_render_results(self, fetch_fields, mapping, icon, limit): icon_per_type = { 'infographic': 'fa-file-picture-o', 'webpage': 'fa-file-text', @@ -913,18 +926,10 @@ class Slide(models.Model): 'quiz': 'fa-question-circle', 'link': 'fa-file-code-o', # appears in template "slide_icon" } - def patch_slide(slide, data): + results_data = super()._search_render_results(fetch_fields, mapping, icon, limit) + for slide, data in zip(self, results_data): data['_fa'] = icon_per_type.get(slide.slide_type, 'fa-file-pdf-o') data['url'] = slide.website_url data['course'] = _('Course: %s', slide.channel_id.name) data['course_url'] = slide.channel_id.website_url - return { - 'model': 'slide.slide', - 'base_domain': [website.website_domain()], - 'search_fields': search_fields, - 'fetch_fields': fetch_fields, - 'patch_data_function': patch_slide, - 'mapping': mapping, - 'icon': 'fa-shopping-cart', - 'order': 'name desc, id desc' if 'name desc' in order else 'name asc, id desc', - } + return results_data