views.py 13 KB

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