Browse Source

Overhauled search. #149

Ralfp 12 years ago
parent
commit
aab66311d8

+ 0 - 1
deployment/settings.py

@@ -57,7 +57,6 @@ HAYSTACK_CONNECTIONS = {
     'default': {
     'default': {
         'ENGINE': 'haystack.backends.whoosh_backend.WhooshEngine', # Misago uses Whoosh by default
         'ENGINE': 'haystack.backends.whoosh_backend.WhooshEngine', # Misago uses Whoosh by default
         'PATH': 'searchindex',
         'PATH': 'searchindex',
-        'INCLUDE_SPELLING': True,
     },
     },
 }
 }
 
 

+ 2 - 2
misago/apps/admin/stats/views.py

@@ -74,7 +74,7 @@ def graph(request, model, date_start, date_end, precision):
     """
     """
     if date_start == date_end:
     if date_start == date_end:
         # Bad dates
         # Bad dates
-        raise error404()
+        raise error404(RequestContext)
 
 
     # Turn stuff into datetime's
     # Turn stuff into datetime's
     date_start = datetime.strptime(date_start, '%Y-%m-%d')
     date_start = datetime.strptime(date_start, '%Y-%m-%d')
@@ -98,7 +98,7 @@ def graph(request, model, date_start, date_end, precision):
 
 
     if not model in models_map or check_dates(date_start, date_end, precision):
     if not model in models_map or check_dates(date_start, date_end, precision):
         # Bad model name or graph data!
         # Bad model name or graph data!
-        raise error404()
+        raise error404(request)
 
 
     form = GenerateStatisticsForm(
     form = GenerateStatisticsForm(
                                   provider_choices=statistics_providers,
                                   provider_choices=statistics_providers,

+ 0 - 24
misago/apps/privatethreads/search.py

@@ -1,24 +0,0 @@
-from misago.models import Post
-from misago.acl.exceptions import ACLError404
-from misago.apps.search.views import SearchBaseView, ResultsBaseView
-
-class SearchPrivateThreadsMixin(object):
-    search_route = 'private_threads_search'
-    results_route = 'private_threads_results'
-
-    def check_acl(self):
-        if not (self.request.acl.private_threads.can_participate()
-                and self.request.settings['enable_private_threads']):
-            raise ACLError404()
-
-    def filter_queryset(self, sqs):
-        threads = [t.pk for t in self.request.user.private_thread_set.all()]
-        return sqs.filter(thread_id__in=threads)
-
-
-class SearchView(SearchPrivateThreadsMixin, SearchBaseView):
-    pass
-
-
-class ResultsView(SearchPrivateThreadsMixin, ResultsBaseView):
-    pass

+ 0 - 4
misago/apps/privatethreads/urls.py

@@ -37,8 +37,4 @@ urlpatterns = patterns('misago.apps.privatethreads',
     url(r'^(?P<slug>(\w|-)+)-(?P<thread>\d+)/(?P<post>\d+)/changelog/$', 'changelog.ChangelogView', name="private_thread_changelog"),
     url(r'^(?P<slug>(\w|-)+)-(?P<thread>\d+)/(?P<post>\d+)/changelog/$', 'changelog.ChangelogView', name="private_thread_changelog"),
     url(r'^(?P<slug>(\w|-)+)-(?P<thread>\d+)/(?P<post>\d+)/changelog/(?P<change>\d+)/$', 'changelog.ChangelogDiffView', name="private_thread_changelog_diff"),
     url(r'^(?P<slug>(\w|-)+)-(?P<thread>\d+)/(?P<post>\d+)/changelog/(?P<change>\d+)/$', 'changelog.ChangelogDiffView', name="private_thread_changelog_diff"),
     url(r'^(?P<slug>(\w|-)+)-(?P<thread>\d+)/(?P<post>\d+)/changelog/(?P<change>\d+)/revert/$', 'changelog.ChangelogRevertView', name="private_thread_changelog_revert"),
     url(r'^(?P<slug>(\w|-)+)-(?P<thread>\d+)/(?P<post>\d+)/changelog/(?P<change>\d+)/revert/$', 'changelog.ChangelogRevertView', name="private_thread_changelog_revert"),
-    # Extra search routes
-    url(r'^search/$', 'search.SearchView', name="private_threads_search"),
-    url(r'^search/results/$', 'search.SearchView', name="private_threads_results"),
-    url(r'^search/results/(?P<page>[1-9]([0-9]+)?)/$', 'search.ResultsView', name="private_threads_results"),
 )
 )

+ 0 - 22
misago/apps/reports/search.py

@@ -1,22 +0,0 @@
-from misago.models import Forum, Post
-from misago.acl.exceptions import ACLError404
-from misago.apps.search.views import SearchBaseView, ResultsBaseView
-
-class SearchReportsMixin(object):
-    search_route = 'reports_search'
-    results_route = 'reports_results'
-    
-    def check_acl(self):
-        if not self.request.acl.reports.can_handle():
-            raise ACLError404()
-
-    def filter_queryset(self, sqs):
-        return sqs.filter(forum=Forum.objects.special_pk('reports'))
-
-
-class SearchView(SearchReportsMixin, SearchBaseView):
-    pass
-
-
-class ResultsView(SearchReportsMixin, ResultsBaseView):
-    pass

+ 0 - 4
misago/apps/reports/urls.py

@@ -29,8 +29,4 @@ urlpatterns = patterns('misago.apps.reports',
     url(r'^(?P<slug>(\w|-)+)-(?P<thread>\d+)/(?P<post>\d+)/changelog/$', 'changelog.ChangelogView', name="report_changelog"),
     url(r'^(?P<slug>(\w|-)+)-(?P<thread>\d+)/(?P<post>\d+)/changelog/$', 'changelog.ChangelogView', name="report_changelog"),
     url(r'^(?P<slug>(\w|-)+)-(?P<thread>\d+)/(?P<post>\d+)/changelog/(?P<change>\d+)/$', 'changelog.ChangelogDiffView', name="report_changelog_diff"),
     url(r'^(?P<slug>(\w|-)+)-(?P<thread>\d+)/(?P<post>\d+)/changelog/(?P<change>\d+)/$', 'changelog.ChangelogDiffView', name="report_changelog_diff"),
     url(r'^(?P<slug>(\w|-)+)-(?P<thread>\d+)/(?P<post>\d+)/changelog/(?P<change>\d+)/revert/$', 'changelog.ChangelogRevertView', name="report_changelog_revert"),
     url(r'^(?P<slug>(\w|-)+)-(?P<thread>\d+)/(?P<post>\d+)/changelog/(?P<change>\d+)/revert/$', 'changelog.ChangelogRevertView', name="report_changelog_revert"),
-    # Extra search routes
-    url(r'^search/$', 'search.SearchView', name="reports_search"),
-    url(r'^search/results/$', 'search.SearchView', name="reports_results"),
-    url(r'^search/results/(?P<page>[1-9]([0-9]+)?)/$', 'search.ResultsView', name="reports_results"),
 )
 )

+ 7 - 0
misago/apps/search/urls.py

@@ -0,0 +1,7 @@
+from django.conf.urls import patterns, url
+
+urlpatterns = patterns('misago.apps.search.views',
+    url(r'^$', 'QuickSearchView', name="search"),
+    url(r'^results/$', 'SearchResultsView', name="search_results"),
+    url(r'^results/(?P<page>[1-9]([0-9]+)?)/$', 'SearchResultsView', name="search_results"),
+)

+ 73 - 52
misago/apps/search/views.py

@@ -4,6 +4,7 @@ from django.shortcuts import redirect
 from django.template import RequestContext
 from django.template import RequestContext
 from django.utils import timezone
 from django.utils import timezone
 from django.utils.translation import ugettext as _
 from django.utils.translation import ugettext as _
+from haystack.inputs import AutoQuery
 from haystack.query import SearchQuerySet, RelatedSearchQuerySet
 from haystack.query import SearchQuerySet, RelatedSearchQuerySet
 from misago.acl.exceptions import ACLError403, ACLError404
 from misago.acl.exceptions import ACLError403, ACLError404
 from misago.decorators import block_crawlers
 from misago.decorators import block_crawlers
@@ -15,33 +16,56 @@ from misago.apps.errors import error403, error404
 from misago.apps.profiles.views import list as users_list
 from misago.apps.profiles.views import list as users_list
 from misago.apps.search.forms import QuickSearchForm
 from misago.apps.search.forms import QuickSearchForm
 
 
-
 class ViewBase(object):
 class ViewBase(object):
     search_route = 'search'
     search_route = 'search'
-    results_route = 'search_results'
-    advanced_route = None
 
 
     def check_acl(self):
     def check_acl(self):
         pass
         pass
 
 
-    def queryset(self):
-        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))
+        else:
+            sqs = sqs.auto_query(search_query)
+
+        if self.request.POST.get('search_in') == 'private':
+            if not (self.request.acl.private_threads.can_participate()
+                    and self.request.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:
+                thread_id = int(self.request.POST.get('search_thread'))
+                thread_clean = Thread.objects.get(id=thread_id)
+                if not thread_clean.forum_id in Forum.objects.readable_forums(self.request.acl, True):
+                    raise ACLError404()
+                self.thread_clean = thread_clean
+                sqs = sqs.filter(thread=thread_clean.pk)
+            except (TypeError, Thread.DoesNotExist):
+                raise ACLError404()
+        else:
+            sqs = sqs.filter(forum__in=Forum.objects.readable_forums(self.request.acl))
 
 
-    def search_form_type(self):
-        return QuickSearchForm
+        if self.request.POST.get('search_author'):
+            sqs = sqs.filter(author__exact=self.request.POST.get('search_author'))
+
+        return sqs
 
 
     def render_to_response(self, template, form, context):
     def render_to_response(self, template, form, context):
-        tpl_dict = {
-                    'form': FormFields(form),
-                    'search_route': self.search_route,
-                    'results_route': self.results_route,
-                    'search_advanced': self.advanced_route,
-                    'suggestion': None,
-                    'disable_search': True,
-                    }
-        tpl_dict.update(context)
+        for i in ('search_query', 'search_in', 'search_author', 'search_thread_titles'):
+            if self.request.POST.get(i):
+                context[i] = self.request.POST.get(i)
+        try:
+            context['search_thread'] = self.thread_clean
+        except AttributeError:
+            pass
         return self.request.theme.render_to_response('search/%s.html' % template,
         return self.request.theme.render_to_response('search/%s.html' % template,
-                                                     tpl_dict,
+                                                     context,
                                                      context_instance=RequestContext(self.request))
                                                      context_instance=RequestContext(self.request))
 
 
     def __new__(cls, request, **kwargs):
     def __new__(cls, request, **kwargs):
@@ -63,18 +87,18 @@ class ViewBase(object):
             return error404(request, unicode(e))
             return error404(request, unicode(e))
 
 
 
 
-class SearchBaseView(ViewBase):
+class QuickSearchView(ViewBase):
     def call(self, **kwargs):
     def call(self, **kwargs):
-        form_type = self.search_form_type()
+        form_type = QuickSearchForm
         if self.request.method != "POST":
         if self.request.method != "POST":
-            form = self.search_form_type()(request=self.request)
+            form = QuickSearchForm(request=self.request)
             return self.render_to_response('home', form,  
             return self.render_to_response('home', form,  
                                            {
                                            {
                                             'search_result': self.request.session.get(self.results_route),
                                             'search_result': self.request.session.get(self.results_route),
                                            })
                                            })
         
         
         try:
         try:
-            form = self.search_form_type()(self.request.POST, request=self.request)
+            form = QuickSearchForm(self.request.POST, request=self.request)
             if form.is_valid():
             if form.is_valid():
                 if form.mode == 'forum':
                 if form.mode == 'forum':
                     jump_to = Forum.objects.forum_by_name(form.target, self.request.acl)
                     jump_to = Forum.objects.forum_by_name(form.target, self.request.acl)
@@ -89,49 +113,43 @@ class SearchBaseView(ViewBase):
                     self.request.POST['username'] = form.target
                     self.request.POST['username'] = form.target
                     return users_list(self.request)
                     return users_list(self.request)
 
 
-                sqs = self.filter_queryset(SearchQuerySet().auto_query(form.cleaned_data['search_query'])).load_all()[:60]
-                suggestion = SearchQuerySet().spelling_suggestion(form.cleaned_data['search_query'])
-                
+                sqs = self.make_query(form.cleaned_data['search_query']).load_all()[:60]
+
                 if self.request.user.is_authenticated():
                 if self.request.user.is_authenticated():
                     self.request.user.last_search = timezone.now()
                     self.request.user.last_search = timezone.now()
                     self.request.user.save(force_update=True)
                     self.request.user.save(force_update=True)
                 if self.request.user.is_anonymous():
                 if self.request.user.is_anonymous():
                     self.request.session['last_search'] = timezone.now()
                     self.request.session['last_search'] = timezone.now()
-                
+
                 if not sqs:
                 if not sqs:
-                    raise SearchException(_("Search returned no results. Change search query and try again."), suggestion)
-
-                if (suggestion.lower() == form.cleaned_data['search_query'].lower()
-                        or suggestion.lower() in form.cleaned_data['search_query'].lower()):
-                    suggestion = None
-
-                if suggestion:
-                    new_sqs = self.filter_queryset(SearchQuerySet().auto_query(form.cleaned_data['search_query'])).load_all()[:60]
-                    sqs_len = len(sqs)
-                    new_len = len(new_sqs)
-                    if not new_len or new_len < sqs_len * 0.8:
-                        suggestion = None # We are assuming suggestion is wrong
-
-                self.request.session[self.results_route] = {
-                                                            'search_query': form.cleaned_data['search_query'],
-                                                            'search_suggestion': suggestion,
-                                                            'search_results': [p.object for p in sqs],
-                                                            }
-                return redirect(reverse(self.results_route))
+                    raise SearchException(_("Search returned no results. Change search query and try again."))
+
+                self.request.session['search_results'] = {
+                                                          'search_query': form.cleaned_data['search_query'],
+                                                          '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],
+                                                          }
+                try:
+                    self.request.session['search_results']['search_thread'] = self.thread_clean
+                except AttributeError:
+                    pass
+                return redirect(reverse('search_results'))
             else:
             else:
                 if 'search_query' in form.errors:
                 if 'search_query' in form.errors:
                     raise SearchException(form.errors['search_query'][0])
                     raise SearchException(form.errors['search_query'][0])
                 raise SearchException(form.errors['__all__'][0])
                 raise SearchException(form.errors['__all__'][0])
         except SearchException as e:
         except SearchException as e:
-            return self.render_to_response('error', form,  
-                                           {'message': unicode(e), 'suggestion': unicode(e.suggestion)})
+            return self.render_to_response('error', form,
+                                           {'message': unicode(e)})
 
 
 
 
-class ResultsBaseView(ViewBase):
+class SearchResultsView(ViewBase):
     def call(self, **kwargs):
     def call(self, **kwargs):
-        result = self.request.session.get(self.results_route)
+        result = self.request.session.get('search_results')
         if not result:
         if not result:
-            form = self.search_form_type()(request=self.request)
+            form = QuickSearchForm(request=self.request)
             return self.render_to_response('error', form,  
             return self.render_to_response('error', form,  
                                            {'message': _("No search results were found.")})
                                            {'message': _("No search results were found.")})
 
 
@@ -140,13 +158,16 @@ class ResultsBaseView(ViewBase):
         try:
         try:
             pagination = make_pagination(kwargs.get('page', 0), items_total, 12)
             pagination = make_pagination(kwargs.get('page', 0), items_total, 12)
         except Http404:
         except Http404:
-            return redirect(reverse(self.search_route))
+            return redirect(reverse('search_results'))
 
 
-        form = self.search_form_type()(request=self.request, initial={'search_query': result['search_query']})
+        form = QuickSearchForm(request=self.request, initial={'search_query': result['search_query']})
         return self.render_to_response('results', form,  
         return self.render_to_response('results', form,  
                                        {
                                        {
                                         'search_query': result['search_query'],
                                         'search_query': result['search_query'],
-                                        'suggestion': result['search_suggestion'],
+                                        'search_in': result.get('search_in'),
+                                        'search_author': result.get('search_author'),
+                                        'search_thread_titles': result.get('search_thread_titles'),
+                                        'search_thread': result.get('search_thread'),
                                         'results': items[pagination['start']:pagination['stop']],
                                         'results': items[pagination['start']:pagination['stop']],
                                         'items_total': items_total,
                                         'items_total': items_total,
                                         'pagination': pagination,
                                         'pagination': pagination,

+ 0 - 14
misago/apps/threads/search.py

@@ -1,14 +0,0 @@
-from misago.models import Forum, Post
-from misago.apps.search.views import SearchBaseView, ResultsBaseView
-
-class SearchThreadsMixin(object):
-    def filter_queryset(self, sqs):
-        return sqs.filter(forum__in=Forum.objects.readable_forums(self.request.acl))
-
-
-class SearchView(SearchThreadsMixin, SearchBaseView):
-    pass
-
-
-class ResultsView(SearchThreadsMixin, ResultsBaseView):
-    pass

+ 0 - 4
misago/apps/threads/urls.py

@@ -38,8 +38,4 @@ urlpatterns = patterns('misago.apps.threads',
     url(r'^thread/(?P<slug>(\w|-)+)-(?P<thread>\d+)/(?P<post>\d+)/changelog/$', 'changelog.ChangelogView', name="thread_changelog"),
     url(r'^thread/(?P<slug>(\w|-)+)-(?P<thread>\d+)/(?P<post>\d+)/changelog/$', 'changelog.ChangelogView', name="thread_changelog"),
     url(r'^thread/(?P<slug>(\w|-)+)-(?P<thread>\d+)/(?P<post>\d+)/changelog/(?P<change>\d+)/$', 'changelog.ChangelogDiffView', name="thread_changelog_diff"),
     url(r'^thread/(?P<slug>(\w|-)+)-(?P<thread>\d+)/(?P<post>\d+)/changelog/(?P<change>\d+)/$', 'changelog.ChangelogDiffView', name="thread_changelog_diff"),
     url(r'^thread/(?P<slug>(\w|-)+)-(?P<thread>\d+)/(?P<post>\d+)/changelog/(?P<change>\d+)/revert/$', 'changelog.ChangelogRevertView', name="thread_changelog_revert"),
     url(r'^thread/(?P<slug>(\w|-)+)-(?P<thread>\d+)/(?P<post>\d+)/changelog/(?P<change>\d+)/revert/$', 'changelog.ChangelogRevertView', name="thread_changelog_revert"),
-    # Searching
-    url(r'^search/$', 'search.SearchView', name="search"),
-    url(r'^search/results/$', 'search.ResultsView', name="search_results"),
-    url(r'^search/results/(?P<page>[1-9]([0-9]+)?)/$', 'search.ResultsView', name="search_results"),
 )
 )

+ 2 - 2
misago/apps/threadtype/jumps.py

@@ -353,11 +353,11 @@ class ShowPostReportBaseView(JumpView):
         @block_guest
         @block_guest
         def view(request):
         def view(request):
             if not self.post.reported:
             if not self.post.reported:
-                return error404()
+                return error404(request)
             reports = Forum.objects.special_model('reports')
             reports = Forum.objects.special_model('reports')
             self.request.acl.forums.allow_forum_view(reports)
             self.request.acl.forums.allow_forum_view(reports)
             report = self.post.live_report()
             report = self.post.live_report()
             if not report:
             if not report:
-                return error404()
+                return error404(request)
             return redirect(reverse('report', kwargs={'thread': report.pk, 'slug': report.slug}))
             return redirect(reverse('report', kwargs={'thread': report.pk, 'slug': report.slug}))
         return view(self.request)
         return view(self.request)

+ 3 - 2
misago/search_indexes.py

@@ -4,14 +4,15 @@ from misago.models import Post
 class PostIndex(indexes.SearchIndex, indexes.Indexable):
 class PostIndex(indexes.SearchIndex, indexes.Indexable):
     text = indexes.CharField(document=True, use_template=True)
     text = indexes.CharField(document=True, use_template=True)
     forum = indexes.IntegerField(model_attr='forum_id')
     forum = indexes.IntegerField(model_attr='forum_id')
-    thread = indexes.CharField(model_attr='thread')
+    thread = indexes.IntegerField(model_attr='thread_id')
+    thread_name = indexes.CharField(model_attr='thread')
     user = indexes.CharField(model_attr='user_name')
     user = indexes.CharField(model_attr='user_name')
     date = indexes.DateTimeField(model_attr='date')
     date = indexes.DateTimeField(model_attr='date')
 
 
     def get_model(self):
     def get_model(self):
         return Post
         return Post
 
 
-    def prepare_thread(self, obj):
+    def prepare_thread_name(self, obj):
         return obj.thread.name
         return obj.thread.name
 
 
     def get_updated_field(self):
     def get_updated_field(self):

+ 1 - 0
misago/urls.py

@@ -29,6 +29,7 @@ urlpatterns += patterns('',
     (r'^reset-password/', include('misago.apps.resetpswd.urls')),
     (r'^reset-password/', include('misago.apps.resetpswd.urls')),
     (r'^private-threads/', include('misago.apps.privatethreads.urls')),
     (r'^private-threads/', include('misago.apps.privatethreads.urls')),
     (r'^reports/', include('misago.apps.reports.urls')),
     (r'^reports/', include('misago.apps.reports.urls')),
+    (r'^search/', include('misago.apps.search.urls')),
     (r'^', include('misago.apps.threads.urls')),
     (r'^', include('misago.apps.threads.urls')),
 )
 )
 
 

+ 13 - 6
static/cranefly/css/cranefly.css

@@ -872,9 +872,16 @@ footer .container .credits p{margin-bottom:0px;color:#555555;font-size:90%;}foot
 ::-moz-selection{background:#f89406;color:#ffffff;}
 ::-moz-selection{background:#f89406;color:#ffffff;}
 .navbar-header .navbar-inner{background:none;background-color:#f3f3f3;border-bottom:1px solid #dfdfdf;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;}.navbar-header .navbar-inner .container{background:url("../img/logo.png");background-position:left center;background-repeat:no-repeat;}
 .navbar-header .navbar-inner{background:none;background-color:#f3f3f3;border-bottom:1px solid #dfdfdf;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;}.navbar-header .navbar-inner .container{background:url("../img/logo.png");background-position:left center;background-repeat:no-repeat;}
 .navbar-header .navbar-inner .brand{margin-left:6px;text-shadow:none;}.navbar-header .navbar-inner .brand:link,.navbar-header .navbar-inner .brand:active,.navbar-header .navbar-inner .brand:visited,.navbar-header .navbar-inner .brand:hover{color:#c24a3b;font-size:200%;}.navbar-header .navbar-inner .brand:link span,.navbar-header .navbar-inner .brand:active span,.navbar-header .navbar-inner .brand:visited span,.navbar-header .navbar-inner .brand:hover span{color:#c0c0c0;}
 .navbar-header .navbar-inner .brand{margin-left:6px;text-shadow:none;}.navbar-header .navbar-inner .brand:link,.navbar-header .navbar-inner .brand:active,.navbar-header .navbar-inner .brand:visited,.navbar-header .navbar-inner .brand:hover{color:#c24a3b;font-size:200%;}.navbar-header .navbar-inner .brand:link span,.navbar-header .navbar-inner .brand:active span,.navbar-header .navbar-inner .brand:visited span,.navbar-header .navbar-inner .brand:hover span{color:#c0c0c0;}
-.navbar-header .navbar-inner .navbar-search-form{background-color:#ffffff;border:1px solid #dfdfdf;border-radius:3px;margin-top:9px;padding:1px 0px;color:#333333;}.navbar-header .navbar-inner .navbar-search-form.search-disabled{opacity:0.6;filter:alpha(opacity=60);}
-.navbar-header .navbar-inner .navbar-search-form input,.navbar-header .navbar-inner .navbar-search-form input:disabled{border:none;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;background:none;margin:0px;}
-.navbar-header .navbar-inner .navbar-search-form button{margin:0px;opacity:0.3;filter:alpha(opacity=30);}.navbar-header .navbar-inner .navbar-search-form button:hover,.navbar-header .navbar-inner .navbar-search-form button:active{opacity:0.8;filter:alpha(opacity=80);}
+.navbar-header .navbar-inner .navbar-search-form{height:30px;width:314px;margin-top:9px;overflow:visible;position:relative;}.navbar-header .navbar-inner .navbar-search-form .navbar-search-border{background-color:#ffffff;border:1px solid #c5c5c5;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;position:absolute;z-index:2;}.navbar-header .navbar-inner .navbar-search-form .navbar-search-border .navbar-search-text{border:1px solid #c5c5c5;border-radius:3px;margin:-1px;padding:1px 0px;color:#333333;}.navbar-header .navbar-inner .navbar-search-form .navbar-search-border .navbar-search-text>input{border:none;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;background:none;margin:0px;width:300px;}
+.navbar-header .navbar-inner .navbar-search-form .navbar-search-border:hover,.navbar-header .navbar-inner .navbar-search-form .navbar-search-border.open{-webkit-box-shadow:0px 0px 3px rgba(85, 85, 85, 0.7);-moz-box-shadow:0px 0px 3px rgba(85, 85, 85, 0.7);box-shadow:0px 0px 3px rgba(85, 85, 85, 0.7);}.navbar-header .navbar-inner .navbar-search-form .navbar-search-border:hover .navbar-search-text,.navbar-header .navbar-inner .navbar-search-form .navbar-search-border.open .navbar-search-text{border-color:#49c7fc;margin-bottom:0px;}
+.navbar-header .navbar-inner .navbar-search-form .navbar-search-border:hover .extra,.navbar-header .navbar-inner .navbar-search-form .navbar-search-border.open .extra{display:block;}
+.navbar-header .navbar-inner .navbar-search-form .navbar-search-border>.extra{background:#ffffff;border-radius:3px;display:none;}.navbar-header .navbar-inner .navbar-search-form .navbar-search-border>.extra .extra-form{padding:0px 8px;}.navbar-header .navbar-inner .navbar-search-form .navbar-search-border>.extra .extra-form .control{margin:12px 0px;}.navbar-header .navbar-inner .navbar-search-form .navbar-search-border>.extra .extra-form .control label{color:#555555;font-weight:bold;}
+.navbar-header .navbar-inner .navbar-search-form .navbar-search-border>.extra .extra-form .control select{margin:0px;width:100%;}
+.navbar-header .navbar-inner .navbar-search-form .navbar-search-border>.extra .extra-form .control input[type="text"]{margin:0px;width:282px;}
+.navbar-header .navbar-inner .navbar-search-form .navbar-search-border>.extra .extra-form .control .checkbox{margin-bottom:-3px;}
+.navbar-header .navbar-inner .navbar-search-form .navbar-search-border>.extra .form-actions{border-radius:0px 0px 2px;margin:0px;margin-top:7px;margin-bottom:0px;padding:8px;}.navbar-header .navbar-inner .navbar-search-form .navbar-search-border>.extra .form-actions .btn{margin:0px;}
+.navbar-header .navbar-inner .navbar-search-form .navbar-search-border>.extra .form-actions a:link,.navbar-header .navbar-inner .navbar-search-form .navbar-search-border>.extra .form-actions a:visited{margin-left:7px;position:relative;top:1px;color:#999999;font-weight:bold;}
+.navbar-header .navbar-inner .navbar-search-form .navbar-search-border>.extra .form-actions a:active,.navbar-header .navbar-inner .navbar-search-form .navbar-search-border>.extra .form-actions a:hover{color:#333333;}
 .navbar-header .navbar-inner .navbar-blocks{margin-left:6px;}.navbar-header .navbar-inner .navbar-blocks li{margin-left:6px;}.navbar-header .navbar-inner .navbar-blocks li form{margin:0px;padding:0px;}
 .navbar-header .navbar-inner .navbar-blocks{margin-left:6px;}.navbar-header .navbar-inner .navbar-blocks li{margin-left:6px;}.navbar-header .navbar-inner .navbar-blocks li form{margin:0px;padding:0px;}
 .navbar-header .navbar-inner .navbar-blocks li a:link,.navbar-header .navbar-inner .navbar-blocks li a:visited,.navbar-header .navbar-inner .navbar-blocks li .btn-link{background-color:#f8f8f8;border:1px solid #dadada;border-radius:3px;padding:5px 8px;margin-top:9px;}.navbar-header .navbar-inner .navbar-blocks li a:link i,.navbar-header .navbar-inner .navbar-blocks li a:visited i,.navbar-header .navbar-inner .navbar-blocks li .btn-link i{opacity:0.7;filter:alpha(opacity=70);}
 .navbar-header .navbar-inner .navbar-blocks li a:link,.navbar-header .navbar-inner .navbar-blocks li a:visited,.navbar-header .navbar-inner .navbar-blocks li .btn-link{background-color:#f8f8f8;border:1px solid #dadada;border-radius:3px;padding:5px 8px;margin-top:9px;}.navbar-header .navbar-inner .navbar-blocks li a:link i,.navbar-header .navbar-inner .navbar-blocks li a:visited i,.navbar-header .navbar-inner .navbar-blocks li .btn-link i{opacity:0.7;filter:alpha(opacity=70);}
 .navbar-header .navbar-inner .navbar-blocks li a:link .label,.navbar-header .navbar-inner .navbar-blocks li a:visited .label,.navbar-header .navbar-inner .navbar-blocks li .btn-link .label{background-color:#cf402e;margin-left:4px;padding-left:6px;padding-right:5px;position:relative;bottom:1px;}
 .navbar-header .navbar-inner .navbar-blocks li a:link .label,.navbar-header .navbar-inner .navbar-blocks li a:visited .label,.navbar-header .navbar-inner .navbar-blocks li .btn-link .label{background-color:#cf402e;margin-left:4px;padding-left:6px;padding-right:5px;position:relative;bottom:1px;}
@@ -885,11 +892,11 @@ footer .container .credits p{margin-bottom:0px;color:#555555;font-size:90%;}foot
 .navbar-header .navbar-inner .navbar-blocks li a:hover .label,.navbar-header .navbar-inner .navbar-blocks li a:active .label,.navbar-header .navbar-inner .navbar-blocks li .btn-link:hover .label,.navbar-header .navbar-inner .navbar-blocks li .btn-link:active .label{background-color:#eeeeee;color:#333333;}
 .navbar-header .navbar-inner .navbar-blocks li a:hover .label,.navbar-header .navbar-inner .navbar-blocks li a:active .label,.navbar-header .navbar-inner .navbar-blocks li .btn-link:hover .label,.navbar-header .navbar-inner .navbar-blocks li .btn-link:active .label{background-color:#eeeeee;color:#333333;}
 .navbar-header .navbar-inner .navbar-blocks li.user-profile a:link,.navbar-header .navbar-inner .navbar-blocks li.user-profile a:visited,.navbar-header .navbar-inner .navbar-blocks li.user-profile a:hover,.navbar-header .navbar-inner .navbar-blocks li.user-profile a:active{background:none;border:none;margin-right:8px;margin-top:5px;font-weight:bold;text-shadow:none;}.navbar-header .navbar-inner .navbar-blocks li.user-profile a:link img,.navbar-header .navbar-inner .navbar-blocks li.user-profile a:visited img,.navbar-header .navbar-inner .navbar-blocks li.user-profile a:hover img,.navbar-header .navbar-inner .navbar-blocks li.user-profile a:active img{border-radius:3px;margin-right:6px;width:32px;height:32px;position:relative;bottom:1px;}
 .navbar-header .navbar-inner .navbar-blocks li.user-profile a:link,.navbar-header .navbar-inner .navbar-blocks li.user-profile a:visited,.navbar-header .navbar-inner .navbar-blocks li.user-profile a:hover,.navbar-header .navbar-inner .navbar-blocks li.user-profile a:active{background:none;border:none;margin-right:8px;margin-top:5px;font-weight:bold;text-shadow:none;}.navbar-header .navbar-inner .navbar-blocks li.user-profile a:link img,.navbar-header .navbar-inner .navbar-blocks li.user-profile a:visited img,.navbar-header .navbar-inner .navbar-blocks li.user-profile a:hover img,.navbar-header .navbar-inner .navbar-blocks li.user-profile a:active img{border-radius:3px;margin-right:6px;width:32px;height:32px;position:relative;bottom:1px;}
 .navbar-header .navbar-inner .navbar-compact{display:none;}.navbar-header .navbar-inner .navbar-compact li.user-profile>a:link,.navbar-header .navbar-inner .navbar-compact li.user-profile>a:visited{-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;margin-top:0px;padding:14px;padding-top:10px;padding-bottom:8px;}.navbar-header .navbar-inner .navbar-compact li.user-profile>a:link img,.navbar-header .navbar-inner .navbar-compact li.user-profile>a:visited img{margin-right:0px;margin-left:6px;}
 .navbar-header .navbar-inner .navbar-compact{display:none;}.navbar-header .navbar-inner .navbar-compact li.user-profile>a:link,.navbar-header .navbar-inner .navbar-compact li.user-profile>a:visited{-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;margin-top:0px;padding:14px;padding-top:10px;padding-bottom:8px;}.navbar-header .navbar-inner .navbar-compact li.user-profile>a:link img,.navbar-header .navbar-inner .navbar-compact li.user-profile>a:visited img{margin-right:0px;margin-left:6px;}
-.navbar-header .navbar-inner .navbar-compact li.user-profile>a:link .caret-border,.navbar-header .navbar-inner .navbar-compact li.user-profile>a:visited .caret-border{background-color:#fbfbfb;border:1px solid #dfdfdf;-webkit-border-radius:2px;-moz-border-radius:2px;border-radius:2px;margin-left:8px;padding:0px 4px;}.navbar-header .navbar-inner .navbar-compact li.user-profile>a:link .caret-border .caret,.navbar-header .navbar-inner .navbar-compact li.user-profile>a:visited .caret-border .caret{margin:0px;padding:0px;position:relative;top:13px;}
+.navbar-header .navbar-inner .navbar-compact li.user-profile>a:link .caret-border,.navbar-header .navbar-inner .navbar-compact li.user-profile>a:visited .caret-border{background-color:#fbfbfb;border:1px solid #dfdfdf;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;margin-left:8px;padding:0px 4px;}.navbar-header .navbar-inner .navbar-compact li.user-profile>a:link .caret-border .caret,.navbar-header .navbar-inner .navbar-compact li.user-profile>a:visited .caret-border .caret{margin:0px;padding:0px;position:relative;top:13px;}
 .navbar-header .navbar-inner .navbar-compact li.user-profile>a:hover,.navbar-header .navbar-inner .navbar-compact li.user-profile>a:active{background-color:#fbfbfb;}.navbar-header .navbar-inner .navbar-compact li.user-profile>a:hover .caret-border,.navbar-header .navbar-inner .navbar-compact li.user-profile>a:active .caret-border{border-color:#999999;}
 .navbar-header .navbar-inner .navbar-compact li.user-profile>a:hover,.navbar-header .navbar-inner .navbar-compact li.user-profile>a:active{background-color:#fbfbfb;}.navbar-header .navbar-inner .navbar-compact li.user-profile>a:hover .caret-border,.navbar-header .navbar-inner .navbar-compact li.user-profile>a:active .caret-border{border-color:#999999;}
 .navbar-header .navbar-inner .navbar-compact li.user-profile.open .dropdown-toggle:link,.navbar-header .navbar-inner .navbar-compact li.user-profile.open .dropdown-toggle:visited,.navbar-header .navbar-inner .navbar-compact li.user-profile.open .dropdown-toggle:hover,.navbar-header .navbar-inner .navbar-compact li.user-profile.open .dropdown-toggle:focus{background-color:#fbfbfb;}.navbar-header .navbar-inner .navbar-compact li.user-profile.open .dropdown-toggle:link .caret-border,.navbar-header .navbar-inner .navbar-compact li.user-profile.open .dropdown-toggle:visited .caret-border,.navbar-header .navbar-inner .navbar-compact li.user-profile.open .dropdown-toggle:hover .caret-border,.navbar-header .navbar-inner .navbar-compact li.user-profile.open .dropdown-toggle:focus .caret-border{background:#cf402e;border-color:#cf402e;}.navbar-header .navbar-inner .navbar-compact li.user-profile.open .dropdown-toggle:link .caret-border .caret,.navbar-header .navbar-inner .navbar-compact li.user-profile.open .dropdown-toggle:visited .caret-border .caret,.navbar-header .navbar-inner .navbar-compact li.user-profile.open .dropdown-toggle:hover .caret-border .caret,.navbar-header .navbar-inner .navbar-compact li.user-profile.open .dropdown-toggle:focus .caret-border .caret{border-top-color:#ffffff;}
 .navbar-header .navbar-inner .navbar-compact li.user-profile.open .dropdown-toggle:link,.navbar-header .navbar-inner .navbar-compact li.user-profile.open .dropdown-toggle:visited,.navbar-header .navbar-inner .navbar-compact li.user-profile.open .dropdown-toggle:hover,.navbar-header .navbar-inner .navbar-compact li.user-profile.open .dropdown-toggle:focus{background-color:#fbfbfb;}.navbar-header .navbar-inner .navbar-compact li.user-profile.open .dropdown-toggle:link .caret-border,.navbar-header .navbar-inner .navbar-compact li.user-profile.open .dropdown-toggle:visited .caret-border,.navbar-header .navbar-inner .navbar-compact li.user-profile.open .dropdown-toggle:hover .caret-border,.navbar-header .navbar-inner .navbar-compact li.user-profile.open .dropdown-toggle:focus .caret-border{background:#cf402e;border-color:#cf402e;}.navbar-header .navbar-inner .navbar-compact li.user-profile.open .dropdown-toggle:link .caret-border .caret,.navbar-header .navbar-inner .navbar-compact li.user-profile.open .dropdown-toggle:visited .caret-border .caret,.navbar-header .navbar-inner .navbar-compact li.user-profile.open .dropdown-toggle:hover .caret-border .caret,.navbar-header .navbar-inner .navbar-compact li.user-profile.open .dropdown-toggle:focus .caret-border .caret{border-top-color:#ffffff;}
-.navbar-header .navbar-inner .navbar-compact li.user-profile .dropdown-menu{border:none;-webkit-border-radius:2px;-moz-border-radius:2px;border-radius:2px;border-top:4px solid #cf402e;-webkit-box-shadow:0px 3px 4px #999999;-moz-box-shadow:0px 3px 4px #999999;box-shadow:0px 3px 4px #999999;margin:0px;margin-top:-8px;margin-right:1px;padding:4px 0px;width:270px;}.navbar-header .navbar-inner .navbar-compact li.user-profile .dropdown-menu:before{display:none;}
-.navbar-header .navbar-inner .navbar-compact li.user-profile .dropdown-menu:after{border-bottom:6px solid #cf402e;margin-top:-3px;margin-right:11px;}
+.navbar-header .navbar-inner .navbar-compact li.user-profile .dropdown-menu{border:none;border:1px solid #c5c5c5;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;-webkit-box-shadow:0px 4px 3px rgba(85, 85, 85, 0.7);-moz-box-shadow:0px 4px 3px rgba(85, 85, 85, 0.7);box-shadow:0px 4px 3px rgba(85, 85, 85, 0.7);margin:0px;margin-top:-6px;margin-right:0px;padding:4px 0px;width:270px;}.navbar-header .navbar-inner .navbar-compact li.user-profile .dropdown-menu:before{display:none;}
+.navbar-header .navbar-inner .navbar-compact li.user-profile .dropdown-menu:after{border-bottom:6px solid #ffffff;margin-top:0px;margin-right:11px;}
 .navbar-header .navbar-inner .navbar-compact li.user-profile .dropdown-menu>li{margin:0px;padding:0px;}.navbar-header .navbar-inner .navbar-compact li.user-profile .dropdown-menu>li .label{float:right;margin:0px;margin-top:2px;}
 .navbar-header .navbar-inner .navbar-compact li.user-profile .dropdown-menu>li{margin:0px;padding:0px;}.navbar-header .navbar-inner .navbar-compact li.user-profile .dropdown-menu>li .label{float:right;margin:0px;margin-top:2px;}
 .navbar-header .navbar-inner .navbar-compact li.user-profile .dropdown-menu>li a,.navbar-header .navbar-inner .navbar-compact li.user-profile .dropdown-menu>li .btn-link{-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;clear:none;display:block;float:none;margin:0px;padding:6px 12px;color:#333333;font-weight:normal;text-align:left;}.navbar-header .navbar-inner .navbar-compact li.user-profile .dropdown-menu>li a i,.navbar-header .navbar-inner .navbar-compact li.user-profile .dropdown-menu>li .btn-link i{opacity:1;filter:alpha(opacity=100);}
 .navbar-header .navbar-inner .navbar-compact li.user-profile .dropdown-menu>li a,.navbar-header .navbar-inner .navbar-compact li.user-profile .dropdown-menu>li .btn-link{-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;clear:none;display:block;float:none;margin:0px;padding:6px 12px;color:#333333;font-weight:normal;text-align:left;}.navbar-header .navbar-inner .navbar-compact li.user-profile .dropdown-menu>li a i,.navbar-header .navbar-inner .navbar-compact li.user-profile .dropdown-menu>li .btn-link i{opacity:1;filter:alpha(opacity=100);}
 .navbar-header .navbar-inner .navbar-compact li.user-profile .dropdown-menu>li a:link .label,.navbar-header .navbar-inner .navbar-compact li.user-profile .dropdown-menu>li .btn-link:link .label,.navbar-header .navbar-inner .navbar-compact li.user-profile .dropdown-menu>li a:active .label,.navbar-header .navbar-inner .navbar-compact li.user-profile .dropdown-menu>li .btn-link:active .label,.navbar-header .navbar-inner .navbar-compact li.user-profile .dropdown-menu>li a:visited .label,.navbar-header .navbar-inner .navbar-compact li.user-profile .dropdown-menu>li .btn-link:visited .label,.navbar-header .navbar-inner .navbar-compact li.user-profile .dropdown-menu>li a:hover .label,.navbar-header .navbar-inner .navbar-compact li.user-profile .dropdown-menu>li .btn-link:hover .label{background-color:#cf402e;float:right;color:#ffffff;text-shadow:0px 1px 0px #7c261b;}
 .navbar-header .navbar-inner .navbar-compact li.user-profile .dropdown-menu>li a:link .label,.navbar-header .navbar-inner .navbar-compact li.user-profile .dropdown-menu>li .btn-link:link .label,.navbar-header .navbar-inner .navbar-compact li.user-profile .dropdown-menu>li a:active .label,.navbar-header .navbar-inner .navbar-compact li.user-profile .dropdown-menu>li .btn-link:active .label,.navbar-header .navbar-inner .navbar-compact li.user-profile .dropdown-menu>li a:visited .label,.navbar-header .navbar-inner .navbar-compact li.user-profile .dropdown-menu>li .btn-link:visited .label,.navbar-header .navbar-inner .navbar-compact li.user-profile .dropdown-menu>li a:hover .label,.navbar-header .navbar-inner .navbar-compact li.user-profile .dropdown-menu>li .btn-link:hover .label{background-color:#cf402e;float:right;color:#ffffff;text-shadow:0px 1px 0px #7c261b;}

+ 101 - 27
static/cranefly/css/cranefly/navbar.less

@@ -29,31 +29,105 @@
     }
     }
 
 
     .navbar-search-form {
     .navbar-search-form {
-      background-color: @white;
-      border: 1px solid @navbarBorder;
-      border-radius: @baseBorderRadius;
+      height: 30px;
+      width: @navbarSearchWidth + @baseFontSize;
       margin-top: ((@navbarHeight - @baseLineHeight) / 2) - 6px;
       margin-top: ((@navbarHeight - @baseLineHeight) / 2) - 6px;
-      padding: 1px 0px;
+      overflow: visible;
+      position: relative;
+
+      .navbar-search-border {
+        background-color: @white;
+        border: 1px solid darken(@navbarBorder, 10%);
+        .border-radius(@baseBorderRadius);
+        position: absolute;
+        z-index: 2;
+
+        .navbar-search-text {
+          border: 1px solid darken(@navbarBorder, 10%);
+          border-radius: @baseBorderRadius;
+          margin: -1px;
+          padding: 1px 0px;
 
 
-      color: @textColor;
+          color: @textColor;
 
 
-      &.search-disabled {
-        .opacity(60);
-      }
+          &>input {
+            border: none;
+            .box-shadow(none);
+            background: none;
+            margin: 0px;
+            width: @navbarSearchWidth;
+          }
+        }
 
 
-      input, input:disabled {
-        border: none;
-        .box-shadow(none);
-        background: none;
-        margin: 0px;
-      }
+        &:hover, &.open {
+          .box-shadow(0px 0px 3px fadeout(@gray, 30%));
 
 
-      button {
-        margin: 0px;
-        .opacity(30);
+          .navbar-search-text {
+            border-color: lighten(@blue, 20%);
+            margin-bottom: 0px;
+          }
 
 
-        &:hover, &:active {
-          .opacity(80);
+          .extra {
+            display: block;
+          }
+        }
+
+        &>.extra {
+          background: @white;
+          border-radius: @baseBorderRadius;
+          display: none;
+
+          .extra-form {
+            padding: 0px @navbarSearchPadding;
+
+            .control {
+              margin: (@navbarSearchPadding * 1.5) 0px;
+
+              label {
+                color: @gray;
+                font-weight: bold;
+              }
+
+              select {
+                margin: 0px;
+                width: 100%;
+              }
+
+              input[type="text"] {
+                margin: 0px;
+                width: (@navbarSearchWidth - (@navbarSearchPadding * 2) - 2px);
+              }
+
+              .checkbox {
+                margin-bottom: -3px;
+              }
+            }
+          }
+
+          .form-actions {
+            border-radius: 0px 0px (@baseBorderRadius - 1);
+            margin: 0px;
+            margin-top: (@baseFontSize / 2);
+            margin-bottom: 0px;
+            padding: @navbarSearchPadding;
+
+            .btn {
+              margin: 0px;
+            }
+
+            a:link, a:visited {
+              margin-left: (@baseFontSize / 2);
+              position: relative;
+              top: 1px;
+
+              color: @grayLight;
+              font-weight: bold;
+            }
+
+            a:active, a:hover {
+              color: @textColor;
+            }
+          }
         }
         }
       }
       }
     }
     }
@@ -165,7 +239,7 @@
               .caret-border {
               .caret-border {
                 background-color: @bodyBackground;
                 background-color: @bodyBackground;
                 border: 1px solid @navbarBorder;
                 border: 1px solid @navbarBorder;
-                .border-radius(2px);
+                .border-radius(@baseBorderRadius);
                 margin-left: 8px;
                 margin-left: 8px;
                 padding: 0px 4px;
                 padding: 0px 4px;
 
 
@@ -204,12 +278,12 @@
 
 
           .dropdown-menu {
           .dropdown-menu {
             border: none;
             border: none;
-            .border-radius(2px);
-            border-top: 4px solid @red;
-            .box-shadow(0px 3px 4px @grayLight);
+            border: 1px solid darken(@navbarBorder, 10%);
+            .border-radius(@baseBorderRadius);
+            .box-shadow(0px 4px 3px fadeout(@gray, 30%));
             margin: 0px;
             margin: 0px;
-            margin-top: -8px;
-            margin-right: 1px;
+            margin-top: -6px;
+            margin-right: 0px;
             padding: 4px 0px;
             padding: 4px 0px;
             width: 270px;
             width: 270px;
 
 
@@ -218,8 +292,8 @@
             }
             }
 
 
             &:after {
             &:after {
-              border-bottom: 6px solid @red;
-              margin-top: -3px;
+              border-bottom: 6px solid @white;
+              margin-top: 0px;
               margin-right: 11px;
               margin-right: 11px;
             }
             }
 
 

+ 3 - 0
static/cranefly/css/variables.less

@@ -255,6 +255,9 @@
 
 
 @navbarInverseBrandColor:                @navbarInverseLinkColor;
 @navbarInverseBrandColor:                @navbarInverseLinkColor;
 
 
+@navbarSearchWidth:               300px;
+@navbarSearchPadding:             8px;
+
 // Footer
 // Footer
 // -------------------------
 // -------------------------
 @footerHeight:                        90px;
 @footerHeight:                        90px;

+ 17 - 0
static/cranefly/js/cranefly.js

@@ -25,6 +25,23 @@ $(function () {
   $('.dropdown-clickable').on('click', function (e) {
   $('.dropdown-clickable').on('click', function (e) {
     e.stopPropagation()
     e.stopPropagation()
   });
   });
+
+  // Fancy user nav activation
+  $('#fancy-user-nav').show();
+
+  // Search form extension
+  var nav_search_form = $('#navbar-search');
+  $('#search-field').hover(function() {
+    nav_search_form.addClass('open');
+  });
+
+  $('html').click(function() {
+    nav_search_form.removeClass('open');
+  });
+
+  nav_search_form.click(function(event) {
+    event.stopPropagation();
+  });
   
   
   // Checkbox Group Master
   // Checkbox Group Master
   $('input.checkbox-master').live('click', function(){
   $('input.checkbox-master').live('click', function(){

+ 0 - 3
templates/cranefly/base.html

@@ -15,9 +15,6 @@
   	<script src="{{ STATIC_URL }}cranefly/js/bootstrap.min.js"></script>
   	<script src="{{ STATIC_URL }}cranefly/js/bootstrap.min.js"></script>
     <script type="text/javascript">
     <script type="text/javascript">
       var l_img_broken_msg = "{{ _('Image could not be loaded.') }}";
       var l_img_broken_msg = "{{ _('Image could not be loaded.') }}";
-      $(function() {
-        $('#fancy-user-nav').show();
-      });
     </script>
     </script>
   	<script src="{{ STATIC_URL }}cranefly/js/cranefly.js"></script>{% block javascripts %}{% endblock %}
   	<script src="{{ STATIC_URL }}cranefly/js/cranefly.js"></script>{% block javascripts %}{% endblock %}
     {{ hook_append_extra|safe }}
     {{ hook_append_extra|safe }}

+ 48 - 6
templates/cranefly/layout.html

@@ -9,10 +9,52 @@
         <a href="{% url 'index' %}" class="brand">{% if settings.board_header %}{{ settings.board_header }}{% else %}{{ settings.board_name }}{% endif %}</a>
         <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() %}
         {% if acl.search.can_search() and not user.is_crawler() %}
         <form action="{% url 'search' %}" method="post" class="navbar-form pull-left">
         <form action="{% url 'search' %}" method="post" class="navbar-form pull-left">
-          <div class="navbar-search-form{% if disable_search %} search-disabled{% endif %}">
-            <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
-            <input type="text" name="search_query"{% if disable_search %} disabled="disabled"{% endif %} class="span2" placeholder="{% trans %}Search forums...{% endtrans %}"{% if search_phrase is defined %} value="{{ search_query }}"{% endif %}>
-            <button type="submit"{% if disable_search %} disabled="disabled"{% endif %} class="btn btn-link"><i class="icon-search"></i></button>
+          <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 %}
+                <input type="hidden" name="search_thread" value="{{ thread.pk }}">
+                {% elif search_thread is defined %}
+                <input type="hidden" name="search_thread" value="{{ search_thread.pk }}">
+                {% endif %}
+                <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>
+              <div class="extra">
+                <div class="extra-form">
+                  <div class="control">
+                    <label>{% trans %}Search in{% endtrans %}:</label>
+                    <select name="search_in">
+                      <option value="forums"{% if not search_in is defined or search_in == 'forums' %} selected="selected"{% endif %}>Forums</option>
+                      {% if settings.enable_private_threads and acl.private_threads.can_participate()%}
+                      <option value="private"{% if search_in == 'private' %} selected="selected"{% endif %}>Private threads</option>
+                      {% endif %}
+                      {% if acl.reports.can_handle() %}
+                      <option value="reports"{% if search_in == 'reports' %} selected="selected"{% endif %}>Reports</option>
+                      {% endif %}
+                      {% if thread is defined %}
+                      <option value="thread"{% if search_in == 'thread' %} selected="selected"{% endif %}>{% trans %}This thread{% endtrans %}</option>
+                      {% elif search_thread is defined %}
+                      <option value="thread"{% if search_in == 'thread' %} selected="selected"{% endif %}>{% trans thread=search_thread.name %}In thread "{{ thread }}"{% endtrans %}</option>
+                      {% endif %}
+                    </select>
+                  </div>
+                  <div class="control">
+                    <label>{% trans %}Author name{% endtrans %}:</label>
+                    <input type="text" name="search_author" placeholder="User name..."{% if search_author is defined and search_author %} value="{{ search_author }}"{% endif %}>
+                  </div>
+                  <div class="control">
+                    <label class="checkbox">
+                      <input name="search_thread_titles" type="checkbox"{% if search_thread_titles is defined and search_thread_titles %} checked="checked"{% endif %}> {% trans %}Search only in threads titles{% endtrans %}
+                    </label>
+                  </div>
+                </div>
+                <div class="form-actions">
+                  <button type="submit" class="btn btn-primary"><i class="icon-search"></i> {% trans %}Search{% endtrans%}</button>
+                  {#<a href="#">{% trans %}Advanced Search{% endtrans %}</a>#}
+                </div>
+              </div>
+            </div>
           </div>
           </div>
         </form>
         </form>
         {% endif %}
         {% endif %}
@@ -21,7 +63,7 @@
           {{ hook_primary_menu_prepend|safe }}
           {{ 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 '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 %}
           <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 acl.search.can_search() and not user.is_crawler() %}
+          {% if 1==2 and 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 %}
           <li><a href="{% url 'search' %}" title="{% trans %}Search Forums{% endtrans %}" class="tooltip-bottom"><i class="icon-search"></i></a></li>{% endif %}
           {% endif %}
           {% endif %}
           <li><a href="{% url 'users' %}" title="{% trans %}Browse Users{% endtrans %}" class="tooltip-bottom"><i class="icon-user"></i></a></li>
           <li><a href="{% url 'users' %}" title="{% trans %}Browse Users{% endtrans %}" class="tooltip-bottom"><i class="icon-user"></i></a></li>
@@ -38,7 +80,7 @@
           {% if user.alerts %}
           {% if user.alerts %}
           <li><a href="{% url 'alerts' %}" title="{% trans %}You have new notifications!{% endtrans %}" class="tooltip-bottom"><i class="icon-asterisk"></i><span class="label label-important">{{ user.alerts }}</span></a></li>
           <li><a href="{% url 'alerts' %}" title="{% trans %}You have new notifications!{% endtrans %}" class="tooltip-bottom"><i class="icon-asterisk"></i><span class="label label-important">{{ user.alerts }}</span></a></li>
           {% endif %}
           {% endif %}
-          {% if settings.enable_private_threads and acl.private_threads.can_participate() and user.unread_pds%}
+          {% if settings.enable_private_threads and acl.private_threads.can_participate() and user.unread_pds %}
           <li><a href="{% url 'private_threads' %}" title="{% trans %}There are unread Private Threads!{% endtrans %}" class="tooltip-bottom"><i class="icon-inbox"></i><span class="label label-important">{{ user.unread_pds }}</span></a></li>
           <li><a href="{% url 'private_threads' %}" title="{% trans %}There are unread Private Threads!{% endtrans %}" class="tooltip-bottom"><i class="icon-inbox"></i><span class="label label-important">{{ user.unread_pds }}</span></a></li>
           {% endif %}
           {% endif %}
           {{ hook_user_menu_important_append|safe }}
           {{ hook_user_menu_important_append|safe }}

+ 2 - 6
templates/cranefly/private_threads/list.html

@@ -9,15 +9,11 @@
 {%- endblock %}
 {%- endblock %}
 
 
 {% block container %}
 {% block container %}
-<div class="page-header header-primary header-search">
+<div class="page-header header-primary">
   <div class="container">
   <div class="container">
     {{ messages_list(messages) }}
     {{ messages_list(messages) }}
     <ul class="breadcrumb" {{ macros.itemprop_bread() }}>
     <ul class="breadcrumb" {{ macros.itemprop_bread() }}>
-      {{ self.breadcrumb() }} <form action="{% url 'private_threads_search' %}" class="form-inline" method="post">
-       <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
-       <input maxlength="255" type="text" name="search_query" class="span4">
-       <button type="submit" class="btn btn-primary">{% trans %}Search{% endtrans %}</button>
-    </form></li>
+      {{ self.breadcrumb() }}</li>
     </ul>
     </ul>
     <h1>{% trans %}Private Threads{% endtrans %}</h1>
     <h1>{% trans %}Private Threads{% endtrans %}</h1>
   </div>
   </div>

+ 2 - 6
templates/cranefly/reports/list.html

@@ -9,17 +9,13 @@
 {%- endblock %}
 {%- endblock %}
 
 
 {% block container %}
 {% block container %}
-<div class="page-header header-primary header-search">
+<div class="page-header header-primary">
   <div class="container">
   <div class="container">
     {{ messages_list(messages) }}
     {{ messages_list(messages) }}
     <ul class="breadcrumb" {{ macros.itemprop_bread() }}>
     <ul class="breadcrumb" {{ macros.itemprop_bread() }}>
       {{ self.breadcrumb() }}</li>
       {{ self.breadcrumb() }}</li>
     </ul>
     </ul>
-    <h1>{% trans %}Reported Posts{% endtrans %} <form action="{% url 'reports_search' %}" class="form-inline" method="post">
-       <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
-       <input maxlength="255" type="text" name="search_query" class="span4">
-       <button type="submit" class="btn btn-primary">{% trans %}Search{% endtrans %}</button>
-    </form></h1>
+    <h1>{% trans %}Reported Posts{% endtrans %}</h1>
   </div>
   </div>
 </div>
 </div>
 
 

+ 1 - 15
templates/cranefly/search/layout.html

@@ -7,25 +7,11 @@
 {% block container %}
 {% block container %}
 <div class="page-header header-primary header-search">
 <div class="page-header header-primary header-search">
   <div class="container">
   <div class="container">
-    <h1>{% trans %}Search{% endtrans %} <form action="{{ search_route|url() }}" class="form-inline" method="post">
-       <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
-       {{ forms.input_text(form.fields.search_query, width=6) }}
-       <button type="submit" class="btn btn-primary">{% trans %}Search{% endtrans %}</button>
-    </form></h1>
+    <h1>{% trans %}Search Community{% endtrans %}</h1>
   </div>
   </div>
 </div>
 </div>
 
 
 <div class="container container-primary">
 <div class="container container-primary">
-  {% if suggestion %}
-  <div class="search-suggestion">
-    <p class="lead muted">{% trans %}Did you mean:{% endtrans %}</p>
-    <form action="{{ search_route|url() }}" class="inline-form" method="post">
-      <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
-      <input type="hidden" name="search_query" value="{{ suggestion }}">
-      <button type="submit" class="btn btn-link">{{ suggestion }}</button>
-    </form>
-  </div>
-  {% endif %}
   {% block action %}{% endblock %}
   {% block action %}{% endblock %}
 </div>
 </div>
 {% endblock %}
 {% endblock %}

+ 2 - 2
templates/cranefly/search/results.html

@@ -40,8 +40,8 @@
 <div class="pagination">
 <div class="pagination">
   <ul>
   <ul>
     <li class="count">{{ macros.pager_label(pagination) }}</li>
     <li class="count">{{ macros.pager_label(pagination) }}</li>
-    {%- if pagination['prev'] > 0 %}<li><a href="{%- if pagination['prev'] > 1 %}{{ ('%s_results'|format(search_route))|url(page=pagination['prev']) }}{% else %}{{ ('%s_results'|format(search_route))|url() }}{% endif %}" class="tooltip-top" title="{% trans %}Newer Posts{% endtrans %}"><i class="icon-chevron-left"></i></a></li>{% endif -%}
-    {%- if pagination['next'] > 0 %}<li><a href="{{ ('%s_results'|format(search_route))|url(page=pagination['next']) }}" class="tooltip-top" title="{% trans %}Older Posts{% endtrans %}"><i class="icon-chevron-right"></i></a></li>{% endif -%}
+    {%- if pagination['prev'] > 0 %}<li><a href="{%- if pagination['prev'] > 1 %}{% url 'search_results' page=pagination['prev'] %}{% else %}{% url 'search_results' %}{% endif %}" class="tooltip-top" title="{% trans %}Newer Posts{% endtrans %}"><i class="icon-chevron-left"></i></a></li>{% endif -%}
+    {%- if pagination['next'] > 0 %}<li><a href="{% url 'search_results' page=pagination['next'] %}" class="tooltip-top" title="{% trans %}Older Posts{% endtrans %}"><i class="icon-chevron-right"></i></a></li>{% endif -%}
   </ul>
   </ul>
 </div>
 </div>
 {% endif %}
 {% endif %}