179 lines
6.8 KiB
Python
179 lines
6.8 KiB
Python
# combo - content management system
|
|
# Copyright (C) 2014-2017 Entr'ouvert
|
|
#
|
|
# This program is free software: you can redistribute it and/or modify it
|
|
# under the terms of the GNU Affero General Public License as published
|
|
# by the Free Software Foundation, either version 3 of the License, or
|
|
# (at your option) any later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU Affero General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU Affero General Public License
|
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
import os
|
|
|
|
from django.conf import settings
|
|
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.utils.http import quote
|
|
from django.template import RequestContext, Template
|
|
|
|
from jsonfield import JSONField
|
|
from haystack import connections
|
|
|
|
from combo.utils import requests
|
|
from combo.data.models import CellBase
|
|
from combo.data.library import register_cell_class
|
|
from combo.utils import get_templated_url
|
|
|
|
from . import engines
|
|
|
|
|
|
@register_cell_class
|
|
class SearchCell(CellBase):
|
|
template_name = 'combo/search-cell.html'
|
|
manager_form_template = 'combo/manager/search-cell-form.html'
|
|
|
|
_search_services = JSONField(_('Search Services'), default=dict, blank=True)
|
|
|
|
class Meta:
|
|
verbose_name = _('Search')
|
|
|
|
def is_visible(self, user=None):
|
|
if not self.search_services:
|
|
return False
|
|
return super(SearchCell, self).is_visible(user=user)
|
|
|
|
def get_default_form_class(self):
|
|
from .forms import SearchCellForm
|
|
return SearchCellForm
|
|
|
|
@property
|
|
def varname(self):
|
|
if self.slug:
|
|
# no hyphen in varname, could be used in context and templates
|
|
return self.slug.replace('-', '_')
|
|
return ''
|
|
|
|
@property
|
|
def search_services(self):
|
|
services = []
|
|
for service_slug in self._search_services.get('data') or []:
|
|
service = engines.get(service_slug)
|
|
if service and service.get('url'):
|
|
service['slug'] = service_slug
|
|
services.append(service)
|
|
return services
|
|
|
|
@property
|
|
def has_multiple_search_services(self):
|
|
return len(self._search_services.get('data') or []) > 1
|
|
|
|
@classmethod
|
|
def get_cells_by_search_service(cls, search_service):
|
|
for cell in cls.objects.all():
|
|
if search_service in (cell._search_services.get('data') or []):
|
|
yield cell
|
|
|
|
def modify_global_context(self, context, request):
|
|
# if self.varname is in the query string (of the page),
|
|
# add it to the global context; so it can be used by others cells
|
|
# for example by a JsonCell with ...[self.varname]... in its URL
|
|
if self.varname and self.varname in request.GET:
|
|
context[self.varname] = request.GET.get(self.varname)
|
|
|
|
def get_cell_extra_context(self, context):
|
|
extra_context = super(SearchCell, self).get_cell_extra_context(context)
|
|
# if there is a q_<slug> in query_string, send it to the template (to be
|
|
# used as an initial query) and remove it from query_string
|
|
initial_q = None
|
|
initial_query_string = None
|
|
if context.get('request'):
|
|
request_get = context['request'].GET.copy()
|
|
if self.varname and context.get('request'):
|
|
q_varname = 'q_%s' % self.varname
|
|
if q_varname in request_get:
|
|
initial_q = request_get[q_varname]
|
|
del request_get[q_varname]
|
|
initial_query_string = request_get.urlencode()
|
|
extra_context.update({
|
|
'initial_q': initial_q,
|
|
'initial_query_string': initial_query_string
|
|
})
|
|
return extra_context
|
|
|
|
@classmethod
|
|
def ajax_results_view(cls, request, cell_pk, service_slug):
|
|
cell = cls.objects.get(pk=cell_pk)
|
|
if not cell.is_visible(request.user) or not cell.page.is_visible(request.user):
|
|
raise PermissionDenied
|
|
|
|
query = request.GET.get('q')
|
|
|
|
def render_response(service={}, results={'err': 0, 'data': []}):
|
|
template_names = ['combo/search-cell-results.html']
|
|
if cell.slug:
|
|
template_names.insert(0, 'combo/cells/%s/search-cell-results.html' % cell.slug)
|
|
tmpl = template.loader.select_template(template_names)
|
|
context = {
|
|
'cell': cell,
|
|
'results': results,
|
|
'search_service': service,
|
|
'query': query
|
|
}
|
|
return HttpResponse(tmpl.render(context, request), content_type='text/html')
|
|
|
|
for service in cell.search_services:
|
|
if service.get('slug') == service_slug:
|
|
break
|
|
else:
|
|
return render_response()
|
|
|
|
if not query:
|
|
return render_response(service)
|
|
|
|
url = get_templated_url(service['url'],
|
|
context={'request': request, 'q': query, 'search_service': service})
|
|
url = url % {'q': quote(query.encode('utf-8'))} # if url contains %(q)s
|
|
if url.startswith('/'):
|
|
url = request.build_absolute_uri(url)
|
|
|
|
if not url:
|
|
return render_response(service)
|
|
|
|
kwargs = {}
|
|
kwargs['cache_duration'] = service.get('cache_duration', 0)
|
|
kwargs['remote_service'] = 'auto' if service.get('signature') else None
|
|
# don't automatically add user info to query string, if required it can
|
|
# be set explicitely in the URL template in the engine definition (via
|
|
# {{user_nameid}} or {{user_email}}).
|
|
kwargs['without_user'] = True
|
|
results = requests.get(url, **kwargs).json()
|
|
if service.get('data_key'):
|
|
results['data'] = results.get(service['data_key']) or []
|
|
hit_templates = {}
|
|
if service.get('hit_url_template'):
|
|
hit_templates['url'] = Template(service['hit_url_template'])
|
|
if service.get('hit_label_template'):
|
|
hit_templates['text'] = Template(service['hit_label_template'])
|
|
if service.get('hit_description_template'):
|
|
hit_templates['description'] = Template(service['hit_description_template'])
|
|
if hit_templates:
|
|
for hit in results.get('data') or []:
|
|
for k, v in hit_templates.items():
|
|
hit[k] = v.render(RequestContext(request, hit))
|
|
return render_response(service, results)
|
|
|
|
def has_text_search_service(self):
|
|
return '_text' in self._search_services.get('data', [])
|
|
|
|
def missing_index(self):
|
|
return not os.path.exists(connections['default'].get_backend().path)
|