bug 562667, Collections search on the frontend.
This commit is contained in:
Родитель
6eabe5244d
Коммит
7bf634a532
|
@ -32,6 +32,14 @@ sort_by = (
|
|||
'advanced_search_form_popularity')),
|
||||
)
|
||||
|
||||
collection_sort_by = (
|
||||
('weekly', _lazy('Most popular this week')),
|
||||
('monthly', _lazy('Most popular this month')),
|
||||
('all', _lazy('Most popular all time')),
|
||||
('rating', _lazy('Highest Rated')),
|
||||
('newest', _lazy('Newest')),
|
||||
)
|
||||
|
||||
per_page = (20, 50, 100)
|
||||
|
||||
tuplize = lambda x: divmod(int(x * 10), 10)
|
||||
|
@ -163,3 +171,20 @@ def SearchForm(request):
|
|||
d = request.GET.copy()
|
||||
|
||||
return _SearchForm(d)
|
||||
|
||||
|
||||
class CollectionsSearchForm(forms.Form):
|
||||
q = forms.CharField(widget=forms.HiddenInput, required=False)
|
||||
cat = forms.CharField(widget=forms.HiddenInput)
|
||||
pp = forms.IntegerField(widget=forms.HiddenInput, required=False)
|
||||
sortby = forms.ChoiceField(label=_('Sort By'), choices=collection_sort_by,
|
||||
initial='weekly', required=False)
|
||||
page = forms.IntegerField(widget=forms.HiddenInput, required=False)
|
||||
|
||||
def clean(self):
|
||||
d = self.cleaned_data
|
||||
|
||||
if not d.get('pp'):
|
||||
d['pp'] = per_page[0]
|
||||
|
||||
return d
|
||||
|
|
|
@ -0,0 +1,112 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}
|
||||
{% if query %}
|
||||
{{ page_title(_('Collection Search Results for {0}')|f(query)) }}
|
||||
{% else %}
|
||||
{{ page_title(_('Collection Search Results')) }}
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% block bodyclass %}inverse{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="primary" role="main">
|
||||
<h2>{{ _('Collection Search Results') }}</h2>
|
||||
<div class="featured listing">
|
||||
{% if pager.object_list %}
|
||||
<div class="featured-inner">
|
||||
<div class="listing-header">
|
||||
<div class="num-results">
|
||||
{{ pagination_result_count(pager) }}
|
||||
</div>
|
||||
<form class="item-sort go" action="" method="get">
|
||||
<div>
|
||||
{{ form['q']|safe }}
|
||||
{{ form['cat']|safe }}
|
||||
{{ form['pp']|safe }}
|
||||
<label for="id_sortby">
|
||||
{{ form['sortby'].label }}
|
||||
</label>
|
||||
{{ form['sortby']|safe }}
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
{% for c in pager.object_list %}
|
||||
<div class="item">
|
||||
<div class="item-info">
|
||||
<ul>
|
||||
<li class="addons">
|
||||
{{ _('<strong>{0}</strong> add-ons')|f(c.addon_count)|safe }}
|
||||
</li>
|
||||
<li class="subscribers">
|
||||
{{ _('<strong>{0}</strong> subscribers')|f(c.subscribers)|safe }}
|
||||
</li>
|
||||
<li>
|
||||
{{ barometer(c) }}
|
||||
</li>
|
||||
{% if request.user.is_authenticated() %}
|
||||
<li>
|
||||
{{ collection_favorite(c) }}
|
||||
</li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</div> {# /item-info #}
|
||||
<h3>
|
||||
<a href="{{ c.get_url_path() }}">{{ c.name }}</a>
|
||||
<span>{{ _('created by {0}')|f(c.author|user_link)|safe }}</span>
|
||||
<a href="{{ c.get_url_path() }}">
|
||||
<img class="icon" alt="" src="{{ c.icon_url }}">
|
||||
</a>
|
||||
</h3>
|
||||
<blockquote>{{ c.description|nl2br }}</blockquote>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
<div class="listing-footer">{{ pager|paginator }}</div>
|
||||
{% else %}
|
||||
{% include 'search/no_results.html' %}
|
||||
{% endif %}
|
||||
</div>
|
||||
</div> {# /primary #}
|
||||
|
||||
<div class="secondary" role="complementary">
|
||||
<div class="highlight">
|
||||
<h3>{{ _('What are Collections?') }}</h3>
|
||||
<p>
|
||||
{{ _('Collections are groups of related add-ons assembled for easy sharing.') }}
|
||||
</p>
|
||||
<p>
|
||||
<a class="more-info" href="{{ remora_url('collections/add') }}">
|
||||
{{ _('Create a Collection', 'collections_index_a_create') }}
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="highlight">
|
||||
<h3>{{ _('Add-on Collector', 'collections_index_header_collector') }}</h3>
|
||||
|
||||
<p class="teaser-img">
|
||||
<a href="{{ remora_url('pages/collector') }}">
|
||||
<img alt="" src="{{ MEDIA_URL }}img/amo2009/illustrations/logo-collections-download-146x159.png">
|
||||
</a>
|
||||
</p>
|
||||
<p>
|
||||
{{ _("There's a new way to manage and find favorite add-ons. Comment, share and sync collections, all from your browser.") }}
|
||||
</p>
|
||||
<p>
|
||||
<a class="more-info" href="{{ remora_url('pages/collector') }}">
|
||||
{{ _('Check out Add-on Collector') }}
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
</div> {# /secondary #}
|
||||
|
||||
{# TODO(davedash): parcel this out into a jinja function. #}
|
||||
<div class="secondary">
|
||||
<div id="recently-viewed" class="collections-add">
|
||||
<h3 class="compact-bottom">{{ _('Recently Viewed') }}</h3>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
|
@ -0,0 +1,7 @@
|
|||
<div class="featured-inner">
|
||||
<div class="listing-header">
|
||||
<p class="no-results">
|
||||
{{ _('No results found.') }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
|
@ -19,21 +19,16 @@
|
|||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
{{ addon_listing_items(pager.object_list, show_added_date=(sort == 'newest'),
|
||||
{{ addon_listing_items(pager.object_list,
|
||||
show_added_date=(sort == 'newest'),
|
||||
src='search') }}
|
||||
</div>
|
||||
|
||||
<div class="listing-footer">
|
||||
{{ pager|paginator}}
|
||||
{{ pager|paginator }}
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="featured-inner">
|
||||
<div class="listing-header">
|
||||
<p class="no-results">
|
||||
{{ _('No results found.') }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
{% include 'search/no_results.html' %}
|
||||
{% endif %}
|
||||
|
||||
</div>
|
||||
|
|
|
@ -121,6 +121,7 @@ class GetCategoryIdTest(TestCase):
|
|||
query = lambda *args, **kwargs: SearchClient().query(*args, **kwargs)
|
||||
cquery = lambda *args, **kwargs: CollectionsClient().query(*args, **kwargs)
|
||||
|
||||
|
||||
@mock.patch('search.client.sphinx.SphinxClient')
|
||||
def test_sphinx_timeout(sphinx_mock):
|
||||
def sphinx_error(cls):
|
||||
|
@ -157,6 +158,12 @@ class SearchDownTest(TestCase):
|
|||
doc = pq(resp.content)
|
||||
eq_(doc('.no-results').length, 1)
|
||||
|
||||
def test_collections_search_down(self):
|
||||
self.client.get('/')
|
||||
resp = self.client.get(reverse('search.search') + '?cat=collections')
|
||||
doc = pq(resp.content)
|
||||
eq_(doc('.no-results').length, 1)
|
||||
|
||||
|
||||
class CollectionsSearchTest(SphinxTestCase):
|
||||
|
||||
|
@ -321,6 +328,12 @@ class FrontendSearchTest(SphinxTestCase):
|
|||
# Verify that we have the Refine Results.
|
||||
eq_(doc('.secondary .highlight h3').length, 1)
|
||||
|
||||
def test_default_collections_query(self):
|
||||
r = self.get_response(cat='collections')
|
||||
doc = pq(r.content)
|
||||
eq_(doc('title').text(),
|
||||
'Collection Search Results :: Add-ons for Firefox')
|
||||
|
||||
def test_basic_query(self):
|
||||
"Test a simple query"
|
||||
resp = self.get_response(q='delicious')
|
||||
|
|
|
@ -12,8 +12,8 @@ from amo.helpers import urlparams
|
|||
from amo import urlresolvers
|
||||
from versions.compare import dict_from_int
|
||||
from search import forms
|
||||
from search.client import Client as SearchClient, SearchError
|
||||
from search.forms import SearchForm
|
||||
from search.client import Client as SearchClient, SearchError, CollectionsClient
|
||||
from search.forms import SearchForm, CollectionsSearchForm
|
||||
|
||||
DEFAULT_NUM_RESULTS = 20
|
||||
|
||||
|
@ -146,13 +146,44 @@ def _get_sorts(request, sort):
|
|||
return items
|
||||
|
||||
|
||||
def _collections(request):
|
||||
"""Handle the request for collections."""
|
||||
|
||||
form = CollectionsSearchForm(request.GET)
|
||||
form.is_valid()
|
||||
|
||||
query = form.cleaned_data.get('q', '')
|
||||
|
||||
search_opts = {}
|
||||
search_opts['limit'] = form.cleaned_data.get('pp', DEFAULT_NUM_RESULTS)
|
||||
page = form.cleaned_data.get('page') or 1
|
||||
search_opts['offset'] = (page - 1) * search_opts['limit']
|
||||
search_opts['sort'] = form.cleaned_data.get('sortby')
|
||||
|
||||
client = CollectionsClient()
|
||||
|
||||
try:
|
||||
results = client.query(query, **search_opts)
|
||||
except SearchError, e:
|
||||
return jingo.render(request, 'search/down.html', {}, status=503)
|
||||
|
||||
pager = amo.utils.paginate(request, results, search_opts['limit'])
|
||||
|
||||
c = {
|
||||
'pager': pager,
|
||||
'form': form,
|
||||
}
|
||||
|
||||
|
||||
return jingo.render(request, 'search/collections.html', c)
|
||||
|
||||
|
||||
def search(request):
|
||||
title = _('Search Add-ons')
|
||||
|
||||
# If the form is invalid we still want to have a query.
|
||||
query = request.REQUEST.get('q', '')
|
||||
|
||||
|
||||
search_opts = {
|
||||
'meta': ('versions', 'categories', 'tags'),
|
||||
'version': None
|
||||
|
@ -161,6 +192,22 @@ def search(request):
|
|||
form = SearchForm(request)
|
||||
form.is_valid() # Let the form try to clean data.
|
||||
|
||||
# TODO(davedash): remove this feature when we remove Application for
|
||||
# the search advanced form
|
||||
# Redirect if appid != request.APP.id
|
||||
|
||||
appid = form.cleaned_data['appid']
|
||||
|
||||
if request.APP.id != appid:
|
||||
new_app = amo.APP_IDS.get(appid)
|
||||
return HttpResponseRedirect(
|
||||
urlresolvers.get_app_redirect(new_app))
|
||||
|
||||
category = form.cleaned_data.get('cat')
|
||||
|
||||
if category == 'collections':
|
||||
return _collections(request)
|
||||
|
||||
# TODO: Let's change the form values to something less gross when
|
||||
# Remora dies in a fire.
|
||||
query = form.cleaned_data['q']
|
||||
|
@ -168,18 +215,7 @@ def search(request):
|
|||
if query:
|
||||
title = _('Search for %s' % query)
|
||||
|
||||
appid = form.cleaned_data['appid']
|
||||
|
||||
# TODO(davedash): remove this feature when we remove Application for
|
||||
# the search advanced form
|
||||
# Redirect if appid != request.APP.id
|
||||
if request.APP.id != appid:
|
||||
new_app = amo.APP_IDS.get(appid)
|
||||
return HttpResponseRedirect(
|
||||
urlresolvers.get_app_redirect(new_app))
|
||||
|
||||
addon_type = form.cleaned_data.get('atype', 0)
|
||||
category = form.cleaned_data.get('cat')
|
||||
tag = form.cleaned_data.get('tag')
|
||||
page = form.cleaned_data['page']
|
||||
last_updated = form.cleaned_data.get('lup')
|
||||
|
|
|
@ -280,7 +280,7 @@ $(document).ready(function() {
|
|||
.find('form').submit(callback);
|
||||
});
|
||||
};
|
||||
$('.user-login .barometer form').submit(callback);
|
||||
$('body[data-anonymous["false"]] .barometer form').submit(callback);
|
||||
})
|
||||
|
||||
$(document).ready(function(){
|
||||
|
|
|
@ -318,6 +318,9 @@ MINIFY_BUNDLES = {
|
|||
# Personas
|
||||
'js/zamboni/jquery.hoverIntent.min.js',
|
||||
'js/zamboni/personas.js',
|
||||
|
||||
# Collections
|
||||
'js/zamboni/collections.js',
|
||||
),
|
||||
}
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче