show search suggestions on old templates (i.e., personas)

This commit is contained in:
Chris Van 2011-10-18 14:41:56 -07:00
Родитель bf58cc789d
Коммит 87ae9f1cfe
10 изменённых файлов: 146 добавлений и 50 удалений

Просмотреть файл

@ -22,7 +22,7 @@ from redisutils import mock_redis, reset_redis
import amo
from amo.urlresolvers import Prefixer, get_url_prefix, set_url_prefix
import addons.search
from addons.models import Addon
from addons.models import Addon, Persona
from applications.models import Application, AppVersion
from files.models import File, Platform
from translations.models import Translation
@ -240,6 +240,8 @@ def addon_factory(version_kw={}, file_kw={}, **kw):
a.status = amo.STATUS_PUBLIC
for key, value in kw.items():
setattr(a, key, value)
if key == 'type' and value == amo.ADDON_PERSONA:
Persona.objects.create(addon_id=a.id, persona_id=a.id)
a.save()
return a

Просмотреть файл

@ -1,5 +1,11 @@
{% extends "base.html" %}
{% block search_form %}
{% with skip_autofill=True %}
{% include "search.html" %}
{% endwith %}
{% endblock %}
{% block title %}
{% if query %}
{{ page_title(_('Collection Search Results for {0}')|f(query)) }}

Просмотреть файл

@ -1,4 +1,11 @@
{% extends 'base.html' %}
{% block search_form %}
{% with skip_autofill=True %}
{% include "search.html" %}
{% endwith %}
{% endblock %}
{% block title %}{{ page_title(title) }}{% endblock %}
{% block content %}
<div role="main" class="primary wide">

Просмотреть файл

@ -1,6 +1,12 @@
{% extends "base.html" %}
{% from "browse/macros.html" import secondary_categories %}
{% block search_form %}
{% with skip_autofill=True %}
{% include "search.html" %}
{% endwith %}
{% endblock %}
{% block title %}
{% if query %}
{{ page_title(_('Personas Search Results for {0}')|f(query)) }}

Просмотреть файл

@ -6,12 +6,12 @@ from django.http import QueryDict
from django.test import client
from mock import Mock
from nose import SkipTest
from nose.tools import eq_, nottest
from pyquery import PyQuery as pq
import amo
import amo.tests
from amo.helpers import locale_url
from amo.urlresolvers import reverse
from search.tests import SphinxTestCase
from search import views
@ -266,15 +266,14 @@ class TestWebappSearch(amo.tests.ESTestCase):
self.assertTemplateUsed(r, 'search/mobile/results.html')
@nottest
class TestAjaxSearch(amo.tests.ESTestCase):
@classmethod
def setUpClass(cls):
super(TestSearchSuggestions, cls).setUpClass()
super(TestAjaxSearch, cls).setUpClass()
cls.setUpIndex()
def assert_ajax_query(self, url, params, addons=[],
def search_addons(self, url, params, addons=[],
types=amo.ADDON_SEARCH_TYPES):
r = self.client.get('?'.join([url, params]))
eq_(r.status_code, 200)
@ -299,85 +298,119 @@ class TestAjaxSearch(amo.tests.ESTestCase):
class TestBaseAjaxSearch(TestAjaxSearch):
def assert_ajax_query(self, params, addons=[]):
super(TestBaseAjaxSearch, self).assert_ajax_query(
def search_addons(self, params, addons=[]):
self.refresh()
super(TestBaseAjaxSearch, self).search_addons(
reverse('search.ajax'), params, addons)
def test_ajax_search_by_id(self):
addon = Addon.objects.get(id=4)
self.assert_ajax_query('q=4', [addon])
self.search_addons('q=4', [addon])
def test_ajax_search_by_bad_id(self):
self.assert_ajax_query('q=999', [])
self.search_addons('q=999', [])
def test_ajax_search_unreviewed_by_id(self):
addon = Addon.objects.get(id=4)
addon.update(status=amo.STATUS_UNREVIEWED)
self.assert_ajax_query('q=999', [])
self.search_addons('q=999', [])
def test_ajax_search_lite_reviewed_by_id(self):
addon = Addon.objects.get(id=4)
addon.update(status=amo.STATUS_LITE)
self.assert_ajax_query('q=4', [addon])
self.search_addons('q=4', [addon])
addon.update(status=amo.STATUS_LITE_AND_NOMINATED)
self.assert_ajax_query('q=4', [addon])
self.search_addons('q=4', [addon])
def test_ajax_search_user_disabled_by_id(self):
addon = Addon.objects.get(id=1)
eq_(addon.disabled_by_user, True)
self.assert_ajax_query('q=1', [])
self.search_addons('q=1', [])
def test_ajax_search_admin_disabled_by_id(self):
addon = Addon.objects.get(id=2)
eq_(addon.status, amo.STATUS_DISABLED)
self.assert_ajax_query('q=1', [])
self.search_addons('q=1', [])
def test_ajax_search_personas_by_id(self):
addon = Addon.objects.get(id=4)
addon.update(type=amo.ADDON_PERSONA)
Persona.objects.create(persona_id=4, addon_id=4)
self.assert_ajax_query('q=4', [addon])
self.search_addons('q=4', [addon])
def test_ajax_search_char_limit(self):
self.assert_ajax_query('q=ad', [])
self.search_addons('q=ad', [])
def test_ajax_search_by_name(self):
from nose import SkipTest
raise SkipTest
# Exclude the following:
# 1 (user-disabled), 2 (admin-disabled), 3 (unreviewed).
self.assert_ajax_query('q=add',
list(Addon.objects.filter(id__in=[4, 5, 6])))
self.search_addons('q=add', list(Addon.objects.reviewed()))
def test_ajax_search_by_bad_name(self):
self.assert_ajax_query('q=some+filthy+bad+word', [])
self.search_addons('q=some+filthy+bad+word', [])
class TestSearchSuggestions(TestAjaxSearch):
def setUp(self):
super(TestSearchSuggestions, self).setUp()
self.url = reverse('search.suggestions')
Addon.objects.get(id=4).update(type=amo.ADDON_WEBAPP)
amo.tests.addon_factory(name='addon webapp', type=amo.ADDON_WEBAPP)
amo.tests.addon_factory(name='addon persona', type=amo.ADDON_PERSONA)
amo.tests.addon_factory(name='addon persona', type=amo.ADDON_PERSONA,
disabled_by_user=True, status=amo.STATUS_NULL)
self.refresh()
def assert_ajax_query(self, params, addons=[],
types=amo.ADDON_SEARCH_TYPES):
super(TestBaseAjaxSearch, self).assert_ajax_query(
def search_addons(self, params, addons=[],
types=views.AddonSuggestionsAjax.types):
super(TestSearchSuggestions, self).search_addons(
self.url, params, addons, types)
def search_applications(self, params, apps=[]):
r = self.client.get('?'.join([self.url, params]))
eq_(r.status_code, 200)
data = json.loads(r.content)
data = sorted(data, key=lambda x: x['id'])
apps = sorted(apps, key=lambda x: x.id)
eq_(len(data), len(apps))
for got, expected in zip(data, apps):
eq_(int(got['id']), expected.id)
eq_(got['name'], '%s Add-ons' % unicode(expected.pretty))
eq_(got['url'], locale_url(expected.short))
eq_(got['cls'], 'app ' + expected.short)
def test_get(self):
r = self.client.get(self.url)
eq_(r.status_code, 200)
def test_addons(self):
addons = (Addon.objects.reviewed().exclude(type=amo.ADDON_WEBAPP)
.filter(disabled_by_user=False))
self.assert_ajax_query('q=add', list(addons))
self.assert_ajax_query('q=add&cat=all', list(addons))
addons = (Addon.objects.reviewed()
.filter(disabled_by_user=False,
type__in=views.AddonSuggestionsAjax.types))
self.search_addons('q=add', list(addons))
self.search_addons('q=add&cat=all', list(addons))
def test_personas(self):
personas = (Addon.objects.reviewed()
.filter(type=amo.ADDON_PERSONA, disabled_by_user=False))
self.search_addons('q=add&cat=personas', list(personas),
types=[amo.ADDON_PERSONA])
self.search_addons('q=persona&cat=all', [])
def test_webapps(self):
apps = (Addon.objects.reviewed().filter(type=amo.ADDON_WEBAPP)
.filter(disabled_by_user=False))
self.assert_ajax_query('q=add&cat=apps', list(apps),
types=[amo.ADDON_WEBAPP])
apps = (Addon.objects.reviewed()
.filter(type=amo.ADDON_WEBAPP, disabled_by_user=False))
self.search_addons('q=add&cat=apps', list(apps),
types=[amo.ADDON_WEBAPP])
def test_applications(self):
self.search_applications('', [])
self.search_applications('q=firefox', [amo.FIREFOX])
self.search_applications('q=thunder', [amo.THUNDERBIRD])
self.search_applications('q=monkey', [amo.SEAMONKEY])
self.search_applications('q=sun', [amo.SUNBIRD])
self.search_applications('q=bird', [amo.THUNDERBIRD, amo.SUNBIRD])
self.search_applications('q=mobile', [amo.MOBILE])
self.search_applications('q=mozilla', [])

Просмотреть файл

@ -323,6 +323,10 @@ class AddonSuggestionsAjax(BaseAjaxSearch):
amo.ADDON_DICT, amo.ADDON_SEARCH, amo.ADDON_LPAPP]
class PersonaSuggestionsAjax(BaseAjaxSearch):
types = [amo.ADDON_PERSONA]
class WebappSuggestionsAjax(BaseAjaxSearch):
types = [amo.ADDON_WEBAPP]
@ -339,12 +343,13 @@ def ajax_search(request):
@json_view
def ajax_search_suggestions(request):
# TODO(cvan): Tests will come when I know this is what fligtar wants.
results = []
q = request.GET.get('q')
if q and (q.isdigit() or (not q.isdigit() and len(q) > 2)):
q_ = q.lower()
cat = request.GET.get('cat', 'all')
# Applications.
for a in amo.APP_USAGE:
if q_ in unicode(a.pretty).lower():
@ -358,8 +363,14 @@ def ajax_search_suggestions(request):
# Categories.
cats = (Category.objects
.filter(Q(application=request.APP.id) |
Q(type=amo.ADDON_SEARCH))
.exclude(type=amo.ADDON_WEBAPP))
Q(type=amo.ADDON_SEARCH)))
if cat == 'personas':
cats = cats.filter(type=amo.ADDON_PERSONA)
elif cat == 'apps':
cats = cats.filter(type=amo.ADDON_WEBAPP)
else:
cats = cats.exclude(type__in=[amo.ADDON_PERSONA, amo.ADDON_WEBAPP])
for c in cats:
if not c.name:
continue
@ -373,10 +384,13 @@ def ajax_search_suggestions(request):
'cls': 'cat'
})
if request.GET.get('cat') == 'apps':
results += WebappSuggestionsAjax(request).items
else:
results += AddonSuggestionsAjax(request).items
suggestions = {
'all': AddonSuggestionsAjax,
'personas': PersonaSuggestionsAjax,
'apps': WebappSuggestionsAjax,
}.get(cat, AddonSuggestionsAjax)
results += suggestions(request).items
return results

Просмотреть файл

@ -103,6 +103,7 @@ body {
/** Search */
div.header-search {
position: relative;
margin: 0;
width: 380px;
clear: right;

Просмотреть файл

@ -31,6 +31,24 @@ $.ajaxCache = function(o) {
ajaxFailure: $.noop, // Callback upon failure of Ajax request.
}, o);
if (parseFloat(jQuery.fn.jquery) < 1.5) {
// jqXHR objects allow Deferred methods as of jQuery 1.5. Some of our
// old pages are stuck on jQuery 1.4, so hopefully this'll disappear
// sooner than later.
return $.ajax({
url: o.url,
type: o.method,
data: o.data,
success: function(data) {
o.newItems(data, data);
o.ajaxSuccess(data, items);
},
errors: function(data) {
o.ajaxFailure(data);
}
});
}
var cache = z.AjaxCache(o.url + ':' + o.type),
args = JSON.stringify(o.data),
$self = this,
@ -55,12 +73,8 @@ $.ajaxCache = function(o) {
if (!objEqual(data, cache.previous.data)) {
items = data;
}
if (o.newItems) {
o.newItems(data, items);
}
if (o.ajaxSuccess) {
o.ajaxSuccess(data, items);
}
o.newItems(data, items);
o.ajaxSuccess(data, items);
});
// Optional failure callback.

Просмотреть файл

@ -441,6 +441,7 @@ MINIFY_BUNDLES = {
'css/zamboni/tags.css',
'css/zamboni/tabs.css',
'css/impala/formset.less',
'css/impala/suggestions.less',
),
'zamboni/impala': (
'css/impala/base.css',
@ -582,7 +583,12 @@ MINIFY_BUNDLES = {
'js/global/menu.js',
# Password length and strength
'js/zamboni/password-strength.js'
'js/zamboni/password-strength.js',
# Search suggestions
'js/impala/forms.js',
'js/impala/ajaxcache.js',
'js/impala/suggestions.js',
),
'impala': (
'js/lib/jquery-1.6.4.js',

Просмотреть файл

@ -2,13 +2,20 @@
<form id="search" action="{{ url('search.search') }}">
<span class="wrap">
<input id="search-q" type="text" name="q" required
required autocomplete="off" title=""
class="text {% if not search_form.q.data %}placeholder{% endif %}"
placeholder="{{ search_form.placeholder() }}"
value="{{ search_form.q.data or '' }}">
{{ search_form.cat }}
{{ search_form.appver }}
{{ search_form.platform }}
{% if not skip_autofill %}
{{ search_form.appver }}
{{ search_form.platform }}
{% endif %}
</span>
<input id="search-button" type="image" class="submit search-button" title="{{ _('Search') }}"
src="{{ media('img/zamboni/global/btn-search.png') }}">
{% if waffle.switch('search-suggestions') %}
<div id="site-search-suggestions"
data-src="{{ url('search.suggestions') }}"></div>
{% endif %}
</form>