views.py 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286
  1. from urlparse import urlparse
  2. from django.core.urlresolvers import reverse, resolve
  3. from django.http import Http404
  4. from django.shortcuts import redirect
  5. from django.template import RequestContext
  6. from django.utils import timezone
  7. from django.utils.translation import ugettext as _
  8. from haystack.inputs import AutoQuery
  9. from haystack.query import SearchQuerySet, RelatedSearchQuerySet
  10. from misago.acl.exceptions import ACLError403, ACLError404
  11. from misago.conf import settings
  12. from misago.decorators import block_crawlers
  13. from misago.messages import Message
  14. from misago.models import Forum, Thread, Post, User
  15. from misago.search import SearchException, MisagoSearchQuerySet
  16. from misago.shortcuts import render_to_response
  17. from misago.utils.pagination import make_pagination
  18. from misago.apps.errors import error403, error404
  19. from misago.apps.profiles.views import list as users_list
  20. from misago.apps.search.forms import (QuickSearchForm, ForumsSearchForm,
  21. PrivateThreadsSearchForm, ReportsSearchForm)
  22. class ViewBase(object):
  23. def check_acl(self):
  24. pass
  25. def make_query(self, search_data):
  26. try:
  27. sqs = MisagoSearchQuerySet(self.request.user, self.request.acl)
  28. if self.search_route == 'search_private_threads':
  29. sqs.allow_forum_search(Forum.objects.special_model('private_threads'))
  30. elif self.search_route == 'search_reports':
  31. sqs.allow_forum_search(Forum.objects.special_model('reports'))
  32. else:
  33. if search_data.get('search_forums'):
  34. if search_data.get('search_forums_childs'):
  35. forums_tree = Forum.objects.forums_tree
  36. readable_forums = Forum.objects.readable_forums(self.request.acl)
  37. ranges = []
  38. for forum in search_data.get('search_forums'):
  39. if not ranges or ranges[-1][1] < forum.rght:
  40. ranges.append((forum.lft, forum.rght))
  41. forums = []
  42. for rang in ranges:
  43. for pk, forum in forums_tree.items():
  44. if pk in readable_forums:
  45. if forum.lft >= rang[0] and forum.rght <= rang[1]:
  46. forums.append(pk)
  47. if forum.lft > rang[1]:
  48. break
  49. sqs.in_forums(forums)
  50. else:
  51. sqs.in_forums([f.pk for f in search_data.get('search_forums')])
  52. else:
  53. sqs.in_forums(Forum.objects.readable_forums(self.request.acl))
  54. if search_data.get('search_thread_titles'):
  55. sqs.search_thread_name(search_data.get('search_query'))
  56. sqs.search_thread_titles()
  57. else:
  58. sqs.search_content(search_data.get('search_query'))
  59. if search_data.get('search_thread'):
  60. sqs.search_thread_name_link(search_data.get('search_thread'))
  61. if search_data.get('search_author'):
  62. sqs.search_user_name(search_data.get('search_author'))
  63. if search_data.get('search_before'):
  64. sqs.search_before(search_data.get('search_before'))
  65. if search_data.get('search_after'):
  66. sqs.search_after(search_data.get('search_after'))
  67. return sqs
  68. except Thread.DoesNotExist:
  69. raise ACLError404()
  70. def render_to_response(self, template, form, context):
  71. context['search_route'] = self.search_route
  72. context['form'] = form
  73. for i in ('search_query', 'search_in', 'search_author', 'search_thread_titles'):
  74. if self.request.POST.get(i):
  75. context[i] = self.request.POST.get(i)
  76. try:
  77. context['search_thread'] = self.thread_clean
  78. except AttributeError:
  79. pass
  80. return render_to_response('search/%s.html' % template,
  81. context,
  82. context_instance=RequestContext(self.request))
  83. def draw_form(self, request):
  84. if self.search_route == 'search_quick':
  85. return redirect('search_forums')
  86. search_form_data = self.request.session.get('search_form_data')
  87. if search_form_data and search_form_data['form'] == self.search_route:
  88. form = self.search_form(request=self.request, initial=search_form_data['data'])
  89. else:
  90. form = self.search_form(request=self.request)
  91. return self.render_to_response(self.search_route, form,
  92. {'search_result': self.request.session.get('search_results')})
  93. def search(self, request):
  94. self.request.session['search_form_data'] = None
  95. message = None
  96. # Hackish interception of quick search form
  97. if self.search_route == 'search_quick':
  98. if self.request.POST.get('search_in') == 'thread':
  99. try:
  100. link = resolve(urlparse(self.request.POST.get('search_thread')).path)
  101. search_thread = Thread.objects.get(pk=link.kwargs['thread'])
  102. self.request.acl.threads.allow_thread_view(self.request.user, search_thread)
  103. if search_thread.forum_id == Forum.objects.special_pk('private_threads'):
  104. self.search_route = 'search_private_threads'
  105. self.search_form = PrivateThreadsSearchForm
  106. elif search_thread.forum_id == Forum.objects.special_pk('reports'):
  107. self.search_route = 'search_reports'
  108. self.search_form = ReportsSearchForm
  109. else:
  110. self.search_route = 'search_forums'
  111. self.search_form = ForumsSearchForm
  112. except (Http404, KeyError, Thread.DoesNotExist):
  113. self.search_route = 'search_forums'
  114. self.search_form = ForumsSearchForm
  115. elif self.request.POST.get('search_in') == 'private_threads':
  116. self.search_route = 'search_private_threads'
  117. self.search_form = PrivateThreadsSearchForm
  118. elif self.request.POST.get('search_in') == 'reports':
  119. self.search_route = 'search_reports'
  120. self.search_form = ReportsSearchForm
  121. else:
  122. self.search_route = 'search_forums'
  123. self.search_form = ForumsSearchForm
  124. form = self.search_form(self.request.POST, request=self.request)
  125. try:
  126. if form.is_valid():
  127. sqs = self.make_query(form.cleaned_data).query.load_all().order_by('-date')[:120]
  128. results = []
  129. search_weight = form.cleaned_data.get('search_weight')
  130. for p in sqs:
  131. post = p.object
  132. if search_weight and post.thread.weight not in search_weight:
  133. continue
  134. try:
  135. self.request.acl.threads.allow_post_view(self.request.user, post.thread, post)
  136. results.append(post.pk)
  137. except ACLError404:
  138. pass
  139. if self.request.user.is_authenticated():
  140. self.request.user.last_search = timezone.now()
  141. self.request.user.save(force_update=True)
  142. if self.request.user.is_anonymous():
  143. self.request.session['last_search'] = timezone.now()
  144. self.request.session['search_form_data'] = {'form': self.search_route, 'data': form.cleaned_data}
  145. if not results:
  146. raise SearchException(_("Search returned no results. Change search query and try again."))
  147. self.request.session['search_results'] = {
  148. 'search_query': form.cleaned_data['search_query'],
  149. 'search_route': self.search_route,
  150. 'search_in': self.request.POST.get('search_in'),
  151. 'search_author': self.request.POST.get('search_author'),
  152. 'search_thread_titles': self.request.POST.get('search_thread_titles'),
  153. 'search_results': results,
  154. }
  155. try:
  156. self.request.session['search_results']['search_thread'] = self.thread_clean
  157. except AttributeError:
  158. pass
  159. return redirect(reverse('search_results'))
  160. else:
  161. raise SearchException(form.errors['__all__'][0])
  162. except SearchException as e:
  163. message = Message(e)
  164. return self.render_to_response(self.search_route, form,
  165. {
  166. 'message': message,
  167. 'search_result': self.request.session.get('search_results')
  168. })
  169. def __new__(cls, request, **kwargs):
  170. obj = super(ViewBase, cls).__new__(cls)
  171. return obj(request, **kwargs)
  172. def __call__(self, request, **kwargs):
  173. self.search_route = self.default_search_route
  174. self.search_form = self.default_search_form
  175. try:
  176. self.request = request
  177. if request.user.is_crawler():
  178. raise ACLError404()
  179. self.check_acl()
  180. if not request.acl.search.can_search():
  181. raise ACLError403(_("You don't have permission to search community."))
  182. if self.request.method == "POST":
  183. return self.search(request)
  184. return self.draw_form(request)
  185. except ACLError403 as e:
  186. return error403(request, unicode(e))
  187. except ACLError404 as e:
  188. return error404(request, unicode(e))
  189. class QuickSearchView(ViewBase):
  190. default_search_route = 'search_quick'
  191. default_search_form = QuickSearchForm
  192. class SearchForumsView(ViewBase):
  193. default_search_route = 'search_forums'
  194. default_search_form = ForumsSearchForm
  195. class SearchPrivateThreadsView(ViewBase):
  196. default_search_route = 'search_private_threads'
  197. default_search_form = PrivateThreadsSearchForm
  198. def check_acl(self):
  199. if not self.request.acl.private_threads.can_participate():
  200. raise ACLError404()
  201. class SearchReportsView(ViewBase):
  202. default_search_route = 'search_reports'
  203. default_search_form = ReportsSearchForm
  204. def check_acl(self):
  205. if not self.request.acl.reports.can_handle():
  206. raise ACLError404()
  207. class SearchResultsView(object):
  208. def __new__(cls, request, **kwargs):
  209. obj = super(SearchResultsView, cls).__new__(cls)
  210. return obj(request, **kwargs)
  211. def __call__(self, request, **kwargs):
  212. try:
  213. if request.user.is_crawler():
  214. raise ACLError404()
  215. if not request.acl.search.can_search():
  216. raise ACLError403(_("You don't have permission to search community."))
  217. self.request = request
  218. return self.call(**kwargs)
  219. except ACLError403 as e:
  220. return error403(request, unicode(e))
  221. except ACLError404 as e:
  222. return error404(request, unicode(e))
  223. def call(self, **kwargs):
  224. result = self.request.session.get('search_results')
  225. if not result:
  226. return error404(self.request, _("No search results were found."))
  227. items = result['search_results']
  228. items_total = len(items);
  229. try:
  230. pagination = make_pagination(kwargs.get('page', 0), items_total, 12)
  231. except Http404:
  232. return redirect(reverse('search_results'))
  233. return render_to_response('search/results.html',
  234. {
  235. 'search_in': result.get('search_in'),
  236. 'search_route': result.get('search_route'),
  237. 'search_query': result['search_query'],
  238. 'search_author': result.get('search_author'),
  239. 'search_thread_titles': result.get('search_thread_titles'),
  240. 'search_thread': result.get('search_thread'),
  241. 'results': Post.objects.filter(id__in=items).select_related('forum', 'thread', 'user').order_by('-pk')[pagination['start']:pagination['stop']],
  242. 'items_total': items_total,
  243. 'pagination': pagination,
  244. },
  245. context_instance=RequestContext(self.request))