# 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 . 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 Context, Template from jsonfield import JSONField 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' _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_ 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(Context(hit)) return render_response(service, results)