add internal page contents search service (#6793)

This commit is contained in:
Frédéric Péters 2017-03-04 15:08:01 +01:00
parent 63f6794e8b
commit 6491f69cd1
6 changed files with 77 additions and 14 deletions

View File

@ -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': []}

View File

@ -1,9 +1,9 @@
<div class="links-list">
<ul>
{% for item in results.data %}
<li><a href="{{ item.url }}">{{ item.text }}</a>{% if item.description %}
{{ item.description|safe }}
{% endif %}</li>
<li><a href="{{ item.url }}">{{ item.text }}</a>
{% if item.description %}<div>{{ item.description|safe }}</div>{% endif %}
</li>
{% endfor %}
</ul>
</div>

View File

@ -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<page_pk>\w+)/(?P<cell_reference>[\w_-]+)/$',
views.ajax_page_cell, name='combo-public-ajax-page-cell'),
(r'__style__/$', views.style),

View File

@ -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 = '<p>%s</p>' % page.highlighted['text'][0]
hits.append({
'text': page.title,
'url': page.url,
'description': description,
})
return HttpResponse(json.dumps({'data': hits}), content_type='application/json')

View File

@ -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):

View File

@ -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='<p>foobar baz</p>', order=0)
cell.save()
second_page = Page(title='second page', slug='second-page')
second_page.save()
cell = TextCell(page=second_page, text='<p>other baz</p>', 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