views.py 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284
  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. search_route = 'search'
  24. search_form = None
  25. def check_acl(self):
  26. pass
  27. def make_query(self, search_data):
  28. try:
  29. sqs = MisagoSearchQuerySet(self.request.user, self.request.acl)
  30. if self.search_route == 'search_private_threads':
  31. sqs.allow_forum_search(Forum.objects.special_model('private_threads'))
  32. elif self.search_route == 'search_reports':
  33. sqs.allow_forum_search(Forum.objects.special_model('reports'))
  34. else:
  35. if search_data.get('search_forums'):
  36. if search_data.get('search_forums_childs'):
  37. forums_tree = Forum.objects.forums_tree
  38. readable_forums = Forum.objects.readable_forums(self.request.acl)
  39. ranges = []
  40. for forum in search_data.get('search_forums'):
  41. if not ranges or ranges[-1][1] < forum.rght:
  42. ranges.append((forum.lft, forum.rght))
  43. forums = []
  44. for rang in ranges:
  45. for pk, forum in forums_tree.items():
  46. if pk in readable_forums:
  47. if forum.lft >= rang[0] and forum.rght <= rang[1]:
  48. forums.append(pk)
  49. if forum.lft > rang[1]:
  50. break
  51. sqs.in_forums(forums)
  52. else:
  53. sqs.in_forums([f.pk for f in search_data.get('search_forums')])
  54. else:
  55. sqs.in_forums(Forum.objects.readable_forums(self.request.acl))
  56. if search_data.get('search_thread_titles'):
  57. sqs.search_thread_name(search_data.get('search_query'))
  58. sqs.search_thread_titles()
  59. else:
  60. sqs.search_content(search_data.get('search_query'))
  61. if search_data.get('search_thread'):
  62. sqs.search_thread_name_link(search_data.get('search_thread'))
  63. if search_data.get('search_author'):
  64. sqs.search_user_name(search_data.get('search_author'))
  65. if search_data.get('search_before'):
  66. sqs.search_before(search_data.get('search_before'))
  67. if search_data.get('search_after'):
  68. sqs.search_after(search_data.get('search_after'))
  69. return sqs
  70. except Thread.DoesNotExist:
  71. raise ACLError404()
  72. def render_to_response(self, template, form, context):
  73. context['search_route'] = self.search_route
  74. context['form'] = form
  75. for i in ('search_query', 'search_in', 'search_author', 'search_thread_titles'):
  76. if self.request.POST.get(i):
  77. context[i] = self.request.POST.get(i)
  78. try:
  79. context['search_thread'] = self.thread_clean
  80. except AttributeError:
  81. pass
  82. return render_to_response('search/%s.html' % template,
  83. context,
  84. context_instance=RequestContext(self.request))
  85. def draw_form(self, request):
  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. raise ACLError404()
  114. elif self.request.POST.get('search_in') in ('forums', 'private_threads', 'reports'):
  115. if self.request.POST.get('search_in') == 'forums':
  116. self.search_route = 'search_forums'
  117. self.search_form = ForumsSearchForm
  118. elif self.request.POST.get('search_in') == 'private_threads':
  119. self.search_route = 'search_private_threads'
  120. self.search_form = PrivateThreadsSearchForm
  121. elif self.request.POST.get('search_in') == 'reports':
  122. self.search_route = 'search_reports'
  123. self.search_form = ReportsSearchForm
  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()[: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. try:
  174. self.request = request
  175. if request.user.is_crawler():
  176. raise ACLError404()
  177. self.check_acl()
  178. if not request.acl.search.can_search():
  179. raise ACLError403(_("You don't have permission to search community."))
  180. if self.request.method == "POST":
  181. return self.search(request)
  182. return self.draw_form(request)
  183. except ACLError403 as e:
  184. return error403(request, unicode(e))
  185. except ACLError404 as e:
  186. return error404(request, unicode(e))
  187. class QuickSearchView(ViewBase):
  188. search_route = 'search_quick'
  189. search_form = QuickSearchForm
  190. class SearchForumsView(ViewBase):
  191. search_route = 'search_forums'
  192. search_form = ForumsSearchForm
  193. class SearchPrivateThreadsView(ViewBase):
  194. search_route = 'search_private_threads'
  195. search_form = PrivateThreadsSearchForm
  196. def check_acl(self):
  197. if not self.request.acl.private_threads.can_participate():
  198. raise ACLError404()
  199. class SearchReportsView(ViewBase):
  200. search_route = 'search_reports'
  201. search_form = ReportsSearchForm
  202. def check_acl(self):
  203. if not self.request.acl.reports.can_handle():
  204. raise ACLError404()
  205. class SearchResultsView(object):
  206. def __new__(cls, request, **kwargs):
  207. obj = super(SearchResultsView, cls).__new__(cls)
  208. return obj(request, **kwargs)
  209. def __call__(self, request, **kwargs):
  210. try:
  211. if request.user.is_crawler():
  212. raise ACLError404()
  213. if not request.acl.search.can_search():
  214. raise ACLError403(_("You don't have permission to search community."))
  215. self.request = request
  216. return self.call(**kwargs)
  217. except ACLError403 as e:
  218. return error403(request, unicode(e))
  219. except ACLError404 as e:
  220. return error404(request, unicode(e))
  221. def call(self, **kwargs):
  222. result = self.request.session.get('search_results')
  223. if not result:
  224. return error404(self.request, _("No search results were found."))
  225. items = result['search_results']
  226. items_total = len(items);
  227. try:
  228. pagination = make_pagination(kwargs.get('page', 0), items_total, 12)
  229. except Http404:
  230. return redirect(reverse('search_results'))
  231. return render_to_response('search/results.html',
  232. {
  233. 'search_in': result.get('search_in'),
  234. 'search_route': result.get('search_route'),
  235. 'search_query': result['search_query'],
  236. 'search_author': result.get('search_author'),
  237. 'search_thread_titles': result.get('search_thread_titles'),
  238. 'search_thread': result.get('search_thread'),
  239. 'results': Post.objects.filter(id__in=items).select_related('forum', 'thread', 'user')[pagination['start']:pagination['stop']],
  240. 'items_total': items_total,
  241. 'pagination': pagination,
  242. },
  243. context_instance=RequestContext(self.request))