From cc827903296af8e35504fc0bee3ab3e17004483b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20P=C3=A9ters?= Date: Sat, 4 Mar 2017 15:08:01 +0100 Subject: [PATCH 2/2] add internal page contents search service (#6793) --- combo/apps/search/models.py | 12 ++++--- .../templates/combo/search-cell-results.html | 6 ++-- combo/public/urls.py | 1 + combo/public/views.py | 27 ++++++++++++++ tests/settings.py | 3 ++ tests/test_search.py | 42 ++++++++++++++++++---- 6 files changed, 77 insertions(+), 14 deletions(-) diff --git a/combo/apps/search/models.py b/combo/apps/search/models.py index fe87087..6a42111 100644 --- a/combo/apps/search/models.py +++ b/combo/apps/search/models.py @@ -20,6 +20,7 @@ from django.utils.translation import ugettext_lazy as _ from django import template from django.http import HttpResponse from django.core.exceptions import PermissionDenied +from django.core.urlresolvers import reverse from django.forms import models as model_forms, Select from django.utils.http import quote @@ -37,15 +38,14 @@ class SearchCell(CellBase): class Meta: verbose_name = _('Search') - @classmethod - def is_enabled(cls): - return bool(getattr(settings, 'COMBO_SEARCH_SERVICES', {})) - def is_visible(self, user=None): + if not self.search_service: + return False return super(SearchCell, self).is_visible(user=user) def get_default_form_class(self): search_services = [(None, _('Not configured'))] + search_services.append(('_text', _('Page Contents'))) search_services.extend([(code, service['label']) for code, service in getattr(settings, 'COMBO_SEARCH_SERVICES', {}).items()]) widgets = {'_search_service': Select(choices=search_services)} @@ -62,6 +62,8 @@ class SearchCell(CellBase): @property def search_service(self): + if self._search_service == '_text': + return {'url': reverse('api-search') + '?q=%(q)s', 'label': _('Page Contents')} return settings.COMBO_SEARCH_SERVICES.get(self._search_service) or {} def modify_global_context(self, context, request): @@ -100,6 +102,8 @@ class SearchCell(CellBase): query = request.GET.get('q') if query and cell.search_service.get('url'): url = cell.search_service.get('url') % {'q': quote(query.encode('utf-8'))} + if url.startswith('/'): + url = request.build_absolute_uri(url) results = requests.get(url, cache_duration=0).json() else: results = {'err': 0, 'data': []} diff --git a/combo/apps/search/templates/combo/search-cell-results.html b/combo/apps/search/templates/combo/search-cell-results.html index 37e4b15..2956f5d 100644 --- a/combo/apps/search/templates/combo/search-cell-results.html +++ b/combo/apps/search/templates/combo/search-cell-results.html @@ -1,9 +1,9 @@ diff --git a/combo/public/urls.py b/combo/public/urls.py index c567c96..1cb575b 100644 --- a/combo/public/urls.py +++ b/combo/public/urls.py @@ -20,6 +20,7 @@ from . import views urlpatterns = patterns('combo.publicviews', url(r'^api/menu-badges/', views.menu_badges), + url(r'^api/search/', views.api_search, name='api-search'), url(r'^ajax/cell/(?P\w+)/(?P[\w_-]+)/$', views.ajax_page_cell, name='combo-public-ajax-page-cell'), (r'__style__/$', views.style), diff --git a/combo/public/views.py b/combo/public/views.py index 672039f..7c9afa6 100644 --- a/combo/public/views.py +++ b/combo/public/views.py @@ -38,12 +38,15 @@ else: from django.utils.translation import ugettext as _ from django.forms.widgets import Media +from haystack.query import SearchQuerySet + if 'mellon' in settings.INSTALLED_APPS: from mellon.utils import get_idps else: get_idps = lambda: [] from combo.data.models import CellBase, Page, ParentContentCell, TextCell +from combo.apps.search.models import SearchCell from combo import utils @@ -342,3 +345,27 @@ def menu_badges(request): if badge: badges[cell.page_id] = badge return HttpResponse(json.dumps(badges)) + + +def api_search(request): + for cell in SearchCell.objects.filter(_search_service='_text'): + if not cell.is_visible(request.user): + continue + break + else: + raise Http404() + query = request.GET.get('q') or '' + searchqueryset = SearchQuerySet() + sqs = searchqueryset.auto_query(query).highlight() + sqs.load_all() + hits = [] + for page in sqs: + if page.highlighted['text']: + description = '

%s

' % page.highlighted['text'][0] + hits.append({ + 'text': page.title, + 'url': page.url, + 'description': description, + }) + + return HttpResponse(json.dumps({'data': hits}), content_type='application/json') diff --git a/tests/settings.py b/tests/settings.py index e6c9256..29f43cf 100644 --- a/tests/settings.py +++ b/tests/settings.py @@ -24,6 +24,9 @@ LINGO_SIGNATURE_KEY = '54321' import tempfile MEDIA_ROOT = tempfile.mkdtemp('combo-test') +HAYSTACK_CONNECTIONS['default']['PATH'] = os.path.join( + tempfile.mkdtemp('combo-test-whoosh')) + if 'DISABLE_MIGRATIONS' in os.environ: class DisableMigrations(object): def __contains__(self, item): diff --git a/tests/test_search.py b/tests/test_search.py index 502d02e..9a425c2 100644 --- a/tests/test_search.py +++ b/tests/test_search.py @@ -36,13 +36,6 @@ class SearchServices(object): def __exit__(self, *args, **kwargs): delattr(settings, 'COMBO_SEARCH_SERVICES') -def test_enabled(app): - assert SearchCell.is_enabled() == False - with SearchServices(SEARCH_SERVICES): - assert SearchCell.is_enabled() == True - with SearchServices({}): - assert SearchCell.is_enabled() == False - def test_search_cell(app): with SearchServices(SEARCH_SERVICES): page = Page(title='Search', slug='search_page', template_name='standard') @@ -169,3 +162,38 @@ def test_search_contents_index(): prepared_data = page_index.prepare(page) assert page.title in prepared_data['text'] assert 'foobar' in prepared_data['text'] + +def test_search_api(app): + page = Page(title='example page', slug='example-page') + page.save() + + cell = TextCell(page=page, text='

foobar baz

', order=0) + cell.save() + + second_page = Page(title='second page', slug='second-page') + second_page.save() + + cell = TextCell(page=second_page, text='

other baz

', order=0) + cell.save() + + page_index = PageIndex() + page_index.reindex() + + resp = app.get('/api/search/?q=foobar', status=404) + + cell = SearchCell(page=page, _search_service='_text', order=0) + cell.save() + + resp = app.get('/api/search/?q=foobar', status=200) + assert len(resp.json['data']) == 1 + assert resp.json['data'][0]['text'] == 'example page' + + resp = app.get('/api/search/?q=other', status=200) + assert len(resp.json['data']) == 1 + assert resp.json['data'][0]['text'] == 'second page' + + resp = app.get('/api/search/?q=baz', status=200) + assert len(resp.json['data']) == 2 + + resp = app.get('/api/search/?q=quux', status=200) + assert len(resp.json['data']) == 0 -- 2.11.0