Просмотр исходного кода

Abstracted search query to extra class, exposed links to advanced search.

Rafał Pitoń 11 лет назад
Родитель
Сommit
a6ea035b46

+ 3 - 3
misago/acl/permissions/privatethreads.py

@@ -39,13 +39,13 @@ def make_form(request, role, form):
 
 
 class PrivateThreadsACL(BaseACL):
+    def can_participate(self):
+        return self.acl['can_use_private_threads']
+
     def can_start(self):
         return (self.acl['can_use_private_threads'] and
                 self.acl['can_start_private_threads'])
 
-    def can_participate(self):
-        return self.acl['can_use_private_threads']
-
     def can_invite_ignoring(self):
         return self.acl['can_invite_ignoring']
 

+ 35 - 0
misago/apps/search/searchquery.py

@@ -0,0 +1,35 @@
+from haystack.inputs import AutoQuery
+from haystack.query import SearchQuerySet, RelatedSearchQuerySet
+from misago.acl.exceptions import ACLError403, ACLError404
+from misago.models import Forum, Thread, Post, User
+
+class MisagoSearchQuerySet(object):
+    def __init__(self, user, acl):
+        self.user = user
+        self.acl = acl
+
+    def search_in(self, target):
+        self.target = target
+
+        try:
+            self.allow_forum_search(target)
+        except AttributeError:
+            self.allow_thread_search(target)
+
+    def allow_forum_search(self, target):
+        raise Exception(dir(target))
+
+    def allow_thread_search(self, target):
+        pass
+
+    @property
+    def query(self):
+        try:
+            return self._searchquery
+        except AttributeError:
+            pass
+
+        sqs = SearchQuerySet()
+
+        self._searchquery = sqs
+        return self._searchquery

+ 31 - 51
misago/apps/search/views.py

@@ -10,7 +10,7 @@ from misago.acl.exceptions import ACLError403, ACLError404
 from misago.conf import settings
 from misago.decorators import block_crawlers
 from misago.models import Forum, Thread, Post, User
-from misago.search import SearchException
+from misago.search import SearchException, MisagoSearchQuerySet
 from misago.shortcuts import render_to_response
 from misago.utils.pagination import make_pagination
 from misago.apps.errors import error403, error404
@@ -24,57 +24,29 @@ class ViewBase(object):
         pass
 
     def make_query(self, search_query):
-        sqs = SearchQuerySet()
-        if self.request.POST.get('search_thread_titles'):
-            sqs = sqs.filter(thread_name=AutoQuery(search_query), start_post=1)
-        else:
-            sqs = sqs.auto_query(search_query)
-
-        if self.request.POST.get('search_in') == 'private_threads':
-            if not (self.request.acl.private_threads.can_participate()
-                    and settings.enable_private_threads):
-                raise ACLError404()
-            sqs = sqs.filter(thread__in=[t.pk for t in self.request.user.private_thread_set.all()])
-        elif self.request.POST.get('search_in') == 'reports':
-            if not self.request.acl.reports.can_handle():
-                raise ACLError404()
-            sqs = sqs.filter(forum=Forum.objects.special_pk('reports'))
-        elif self.request.POST.get('search_in') == 'thread':
-            try:
+        try:
+            sqs = MisagoSearchQuerySet(self.request.user, self.request.acl)
+
+            if self.request.POST.get('search_in') == 'thread':
                 thread_id = int(self.request.POST.get('search_thread'))
-                thread_clean = Thread.objects.get(id=thread_id)
-
-                readable_forums = Forum.objects.readable_forums(self.request.acl, True)
-                starter_readable_forums = Forum.objects.starter_readable_forums(self.request.acl)
-                if not thread_clean.forum_id in readable_forums:
-                    if not (thread_clean.forum_id in starter_readable_forums
-                            and thread_clean.start_poster_id
-                            and thread_clean.start_poster_id == self.request.user.id):
-                        raise ACLError404()
-                self.thread_clean = thread_clean
-                sqs = sqs.filter(thread=thread_clean.pk)
-            except (TypeError, Thread.DoesNotExist):
-                raise ACLError404()
-        else:
-            readable_forums = Forum.objects.readable_forums(self.request.acl)
-            starter_readable_forums = Forum.objects.starter_readable_forums(self.request.acl)
-            if not readable_forums and not readable_forums:
-                return error403(request, _("You cannot search any forums."))
-
-            if readable_forums and starter_readable_forums:
-                sqs = sqs.filter(forum__in=starter_readable_forums, thread_starter=self.request.user.id)
-                sqs = sqs.filter_or(forum__in=readable_forums)
-            elif starter_readable_forums:
-                if not self.request.user.is_authenticated():
-                    return error403(request, _("You cannot search any forums."))
-                sqs = sqs.filter(forum__in=starter_readable_forums, thread_starter=self.request.user.id)
+                sqs.search_in(Thread.objects.get(id=thread_id))
+            elif self.request.POST.get('search_in') == 'private_threads':
+                sqs.search_in(Forum.objects.special_model('private_threads'))
+            elif self.request.POST.get('search_in') == 'reports':
+                sqs.search_in(Forum.objects.special_model('reports'))
             else:
-                sqs = sqs.filter(forum__in=readable_forums)
+                sqs.search_in(Forum.objects.special_model('root'))
 
-        if self.request.POST.get('search_author'):
-            sqs = sqs.filter(author__exact=self.request.POST.get('search_author'))
+            if self.request.POST.get('search_thread_titles'):
+                sqs.search_thread_name(search_query)
+            else:
+                sqs.search_content(search_query)
 
-        return sqs
+            if self.request.POST.get('search_author'):
+                sqs.search_user_name(search_query)
+            return sqs
+        except Thread.DoesNotExist:
+            raise ACLError404()
 
     def render_to_response(self, template, form, context):
         for i in ('search_query', 'search_in', 'search_author', 'search_thread_titles'):
@@ -131,7 +103,15 @@ class QuickSearchView(ViewBase):
                     self.request.POST['username'] = form.target
                     return users_list(self.request)
 
-                sqs = self.make_query(form.cleaned_data['search_query']).load_all()[:60]
+                sqs = self.make_query(form.cleaned_data['search_query']).query.load_all()[:120]
+                results = []
+                for p in sqs:
+                    post = p.object
+                    try:
+                        self.request.acl.threads.allow_post_view(self.request.user, post.thread, post)
+                        results.append(post)
+                    except ACLError404:
+                        pass
 
                 if self.request.user.is_authenticated():
                     self.request.user.last_search = timezone.now()
@@ -139,7 +119,7 @@ class QuickSearchView(ViewBase):
                 if self.request.user.is_anonymous():
                     self.request.session['last_search'] = timezone.now()
 
-                if not sqs:
+                if not results:
                     raise SearchException(_("Search returned no results. Change search query and try again."))
 
                 self.request.session['search_results'] = {
@@ -147,7 +127,7 @@ class QuickSearchView(ViewBase):
                                                           'search_in': self.request.POST.get('search_in'),
                                                           'search_author': self.request.POST.get('search_author'),
                                                           'search_thread_titles': self.request.POST.get('search_thread_titles'),
-                                                          'search_results': [p.object for p in sqs],
+                                                          'search_results': results,
                                                           }
                 try:
                     self.request.session['search_results']['search_thread'] = self.thread_clean

+ 114 - 9
misago/search.py

@@ -1,4 +1,9 @@
+from django.db.models import Q
 from django.utils.translation import ugettext_lazy as _
+from haystack.inputs import AutoQuery
+from haystack.query import SearchQuerySet, RelatedSearchQuerySet
+from misago.acl.exceptions import ACLError403, ACLError404
+from misago.models import Forum, Thread, Post, User
 
 class SearchException(Exception):
     def __init__(self, message=None, suggestion=None):
@@ -16,7 +21,7 @@ class SearchQuery(object):
         """
         if raw_query:
             self.parse_query(raw_query)
-        
+
     def parse_query(self, raw_query):
         """
         Parse raw search query into dict of lists of words that should be found and cant be found in string
@@ -27,16 +32,16 @@ class SearchQuery(object):
             word = unicode(word).strip().lower()
             if len(word) == 0:
                 pass
-            
+
             # Find word mode
             mode = '+'
             if word[0] == '-':
                 mode = '-'
                 word = unicode(word[1:]).strip()
-                
+
             # Strip extra crap
             word = ''.join(e for e in word if e.isalnum())
-            
+
             # Slice word?
             if len(word) <= 3:
                 raise SearchException(_("One or more search phrases are shorter than four characters."))
@@ -48,11 +53,11 @@ class SearchQuery(object):
                 if len(word) > 6:
                     word = word[0:-3]
             self.criteria[mode].append(word)
-            
+
         # Complain that there are no positive matches
         if not self.criteria['+'] and not self.criteria['-']:
             raise SearchException(_("Search query is invalid."))
-    
+
     def search(self, value):
         """
         See if value meets search criteria, return True for success and False otherwhise
@@ -69,7 +74,7 @@ class SearchQuery(object):
             return self.search_for(value) and not self.search_against(value)
         except AttributeError:
             raise SearchException(_("You have to define search query before you will be able to search."))
-        
+
     def search_for(self, value):
         """
         See if value is required
@@ -78,7 +83,7 @@ class SearchQuery(object):
             if value.find(word) != -1:
                 return True
         return False
-        
+
     def search_against(self, value):
         """
         See if value is forbidden
@@ -86,4 +91,104 @@ class SearchQuery(object):
         for word in self.criteria['-']:
             if value.find(word) != -1:
                 return True
-        return False
+        return False
+
+
+class MisagoSearchQuerySet(object):
+    def __init__(self, user, acl):
+        self._content = None
+        self._thread_name = None
+        self._user_name = None
+        self._after = None
+        self._before = None
+        self._children = None
+        self._threads = None
+        self._forums = None
+
+        self.user = user
+        self.acl = acl
+
+    def search_in(self, target):
+        try:
+            self.allow_forum_search(target)
+        except AttributeError:
+            self.allow_thread_search(target)
+
+    def allow_forum_search(self, forum):
+        if forum.special == 'private_threads':
+            if not self.acl.private_threads.can_participate():
+                raise ACLError403()
+            if self.acl.private_threads.is_mod():
+                self._threads = [t.pk for t in forum.thread_set.filter(Q(participants__id=self.user.pk) | Q(replies_reported__gt=0)).iterator()]
+            else:
+                self._threads = [t.pk for t in forum.thread_set.filter(participants__id=self.user.pk).iterator()]
+        elif forum.special == 'reports':
+            if not self.acl.reports.can_handle():
+                raise ACLError403()
+            self._forums = [forum.pk]
+        else:
+            self._forums = Forum.objects.readable_forums(self.acl)
+
+    def allow_thread_search(self, thread):
+        self.allow_forum_search(thread.forum)
+        if thread.forum.special == 'private_threads':
+            if thread.pk in self._threads:
+                self._threads = [thread.pk]
+            else:
+                raise ACLError403()
+        self._threads = [thread.pk]
+
+    def search_content(self, query):
+        self._content = query
+
+    def restrict_threads(self, threads=None):
+        self._threads = threads
+
+    def search_thread_name(self, query):
+        self._thread_name = query
+
+    def search_user_name(self, query):
+        self._user_name = query
+
+    def after(self, datetime):
+        self._after = datetime
+
+    def before(self, datetime):
+        self._before = datetime
+
+    def in_forum(self, forum, children=False):
+        self._forum = forum
+        self._children = children
+
+    @property
+    def query(self):
+        try:
+            return self._searchquery
+        except AttributeError:
+            pass
+
+        sqs = SearchQuerySet()
+
+        if self._content:
+            sqs = sqs.auto_query(self._content)
+
+        if self._thread_name:
+            sqs = sqs.filter(thread_name=AutoQuery(self._thread_name), start_post=1)
+
+        if self._user_name:
+            sqs = sqs.filter(username=self._user_name)
+
+        if self._before:
+            sqs = sqs.filter(date__lte=self._before)
+
+        if self._after:
+            sqs = sqs.filter(date__gte=self._after)
+
+        if self._threads:
+            sqs = sqs.filter(thread__in=self._threads)
+
+        if self._forums:
+            sqs = sqs.filter(forum__in=self._forums)
+
+        self._searchquery = sqs
+        return self._searchquery

+ 1 - 0
static/cranefly/css/style.css

@@ -0,0 +1 @@
+lessc: ENOENT, open '/Users/rafapiton/Documents/misago/static/cranefly/css/style.less'

+ 8 - 8
templates/cranefly/layout.html

@@ -12,15 +12,15 @@
         <a href="{{ url('index') }}" class="brand">{% if settings.board_header %}{{ settings.board_header }}{% else %}{{ settings.board_name }}{% endif %}</a>
         {% if acl.search.can_search() and not user.is_crawler() %}
         <form action="{{ url('search') }}" method="post" class="navbar-form pull-left">
+          <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
+          {% if thread is defined and thread %}
+          <input type="hidden" name="search_thread" value="{{ thread.pk }}">
+          {% elif search_thread is defined and search_thread %}
+          <input type="hidden" name="search_thread" value="{{ search_thread.pk }}">
+          {% endif %}
           <div class="navbar-search-form">
             <div id="navbar-search" class="navbar-search-border">
               <div class="navbar-search-text">
-                <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
-                {% if thread is defined and thread %}
-                <input type="hidden" name="search_thread" value="{{ thread.pk }}">
-                {% elif search_thread is defined and search_thread %}
-                <input type="hidden" name="search_thread" value="{{ search_thread.pk }}">
-                {% endif %}
                 <i class="icon-search"></i>
                 <input type="text" id="search-field" name="search_query" placeholder="{% trans %}Search...{% endtrans %}"{% if search_query is defined and search_query %} value="{{ search_query }}"{% endif %}>
               </div>
@@ -55,7 +55,7 @@
                 </div>
                 <div class="form-actions">
                   <button type="submit" class="btn btn-primary"><i class="icon-search"></i> {% trans %}Search{% endtrans%}</button>
-                  {#<a href="{{ url('search') }}">{% trans %}Advanced Search{% endtrans %}</a>#}
+                  <a href="{{ url('search') }}">{% trans %}Advanced Search{% endtrans %}</a>
                 </div>
               </div>
             </div>
@@ -67,7 +67,7 @@
           {{ hook_primary_menu_prepend|safe }}
           <li><a href="{{ url('popular_threads') }}" title="{% trans %}Popular Threads{% endtrans %}" class="hot tooltip-bottom"><i class="icon-fire"></i></a></li>
           <li><a href="{{ url('new_threads') }}" title="{% trans %}New Threads{% endtrans %}" class="fresh tooltip-bottom"><i class="icon-leaf"></i></a></li>{% if not user.crawler %}
-          {% if 1==2 and acl.search.can_search() and not user.is_crawler() %}
+          {% if acl.search.can_search() and not user.is_crawler() %}
           <li><a href="{{ url('search') }}" title="{% trans %}Search Forums{% endtrans %}" class="tooltip-bottom"><i class="icon-search"></i></a></li>{% endif %}
           {% endif %}
           <li><a href="{{ url('users') }}" title="{% trans %}Browse Users{% endtrans %}" class="tooltip-bottom"><i class="icon-group"></i></a></li>