show search suggestions on old templates (i.e., personas)
This commit is contained in:
Родитель
bf58cc789d
Коммит
87ae9f1cfe
|
@ -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>
|
||||
|
|
Загрузка…
Ссылка в новой задаче