views.py 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  1. from django.core.urlresolvers import reverse
  2. from django.http import Http404
  3. from django.shortcuts import redirect
  4. from django.template import RequestContext
  5. from django.utils import timezone
  6. from django.utils.translation import ugettext as _
  7. from haystack.query import SearchQuerySet, RelatedSearchQuerySet
  8. from misago.acl.exceptions import ACLError403, ACLError404
  9. from misago.decorators import block_crawlers
  10. from misago.forms import FormFields
  11. from misago.models import Forum, Thread, Post, User
  12. from misago.search import SearchException
  13. from misago.utils.pagination import make_pagination
  14. from misago.apps.errors import error403, error404
  15. from misago.apps.profiles.views import list as users_list
  16. from misago.apps.search.forms import QuickSearchForm
  17. class ViewBase(object):
  18. search_route = 'search'
  19. results_route = 'search_results'
  20. advanced_route = None
  21. def check_acl(self):
  22. pass
  23. def queryset(self):
  24. pass
  25. def search_form_type(self):
  26. return QuickSearchForm
  27. def render_to_response(self, template, form, context):
  28. tpl_dict = {
  29. 'form': FormFields(form),
  30. 'search_route': self.search_route,
  31. 'results_route': self.results_route,
  32. 'search_advanced': self.advanced_route,
  33. 'suggestion': None,
  34. 'disable_search': True,
  35. }
  36. tpl_dict.update(context)
  37. return self.request.theme.render_to_response('search/%s.html' % template,
  38. tpl_dict,
  39. context_instance=RequestContext(self.request))
  40. def __new__(cls, request, **kwargs):
  41. obj = super(ViewBase, cls).__new__(cls)
  42. return obj(request, **kwargs)
  43. def __call__(self, request, **kwargs):
  44. try:
  45. if request.user.is_crawler():
  46. raise ACLError404()
  47. self.check_acl()
  48. if not request.acl.search.can_search():
  49. raise ACLError403(_("You don't have permission to search community."))
  50. self.request = request
  51. return self.call(**kwargs)
  52. except ACLError403 as e:
  53. return error403(request, unicode(e))
  54. except ACLError404 as e:
  55. return error404(request, unicode(e))
  56. class SearchBaseView(ViewBase):
  57. def call(self, **kwargs):
  58. form_type = self.search_form_type()
  59. if self.request.method != "POST":
  60. form = self.search_form_type()(request=self.request)
  61. return self.render_to_response('home', form,
  62. {
  63. 'search_result': self.request.session.get(self.results_route),
  64. })
  65. try:
  66. form = self.search_form_type()(self.request.POST, request=self.request)
  67. if form.is_valid():
  68. if form.mode == 'forum':
  69. jump_to = Forum.objects.forum_by_name(form.target, self.request.acl)
  70. if jump_to:
  71. if jump_to.level == 1:
  72. return redirect(reverse('index') + ('#%s' % jump_to.slug))
  73. return redirect(jump_to.url)
  74. else:
  75. raise SearchException(_('Forum "%(forum)s" could not be found.') % {'forum': form.target})
  76. if form.mode == 'user':
  77. self.request.POST = self.request.POST.copy()
  78. self.request.POST['username'] = form.target
  79. return users_list(self.request)
  80. sqs = self.filter_queryset(SearchQuerySet().auto_query(form.cleaned_data['search_query'])).load_all()[:60]
  81. suggestion = SearchQuerySet().spelling_suggestion(form.cleaned_data['search_query'])
  82. if self.request.user.is_authenticated():
  83. self.request.user.last_search = timezone.now()
  84. self.request.user.save(force_update=True)
  85. if self.request.user.is_anonymous():
  86. self.request.session['last_search'] = timezone.now()
  87. if not sqs:
  88. raise SearchException(_("Search returned no results. Change search query and try again."), suggestion)
  89. if (suggestion.lower() == form.cleaned_data['search_query'].lower()
  90. or suggestion.lower() in form.cleaned_data['search_query'].lower()):
  91. suggestion = None
  92. if suggestion:
  93. new_sqs = self.filter_queryset(SearchQuerySet().auto_query(form.cleaned_data['search_query'])).load_all()[:60]
  94. sqs_len = len(sqs)
  95. new_len = len(new_sqs)
  96. if not new_len or new_len < sqs_len * 0.8:
  97. suggestion = None # We are assuming suggestion is wrong
  98. self.request.session[self.results_route] = {
  99. 'search_query': form.cleaned_data['search_query'],
  100. 'search_suggestion': suggestion,
  101. 'search_results': [p.object for p in sqs],
  102. }
  103. return redirect(reverse(self.results_route))
  104. else:
  105. if 'search_query' in form.errors:
  106. raise SearchException(form.errors['search_query'][0])
  107. raise SearchException(form.errors['__all__'][0])
  108. except SearchException as e:
  109. return self.render_to_response('error', form,
  110. {'message': unicode(e), 'suggestion': unicode(e.suggestion)})
  111. class ResultsBaseView(ViewBase):
  112. def call(self, **kwargs):
  113. result = self.request.session.get(self.results_route)
  114. if not result:
  115. form = self.search_form_type()(request=self.request)
  116. return self.render_to_response('error', form,
  117. {'message': _("No search results were found.")})
  118. items = result['search_results']
  119. items_total = len(items);
  120. try:
  121. pagination = make_pagination(kwargs.get('page', 0), items_total, 12)
  122. except Http404:
  123. return redirect(reverse(self.search_route))
  124. form = self.search_form_type()(request=self.request, initial={'search_query': result['search_query']})
  125. return self.render_to_response('results', form,
  126. {
  127. 'search_query': result['search_query'],
  128. 'suggestion': result['search_suggestion'],
  129. 'results': items[pagination['start']:pagination['stop']],
  130. 'items_total': items_total,
  131. 'pagination': pagination,
  132. })