2010-02-06 01:06:16 +03:00
|
|
|
import collections
|
|
|
|
import itertools
|
|
|
|
|
2010-04-19 21:06:59 +04:00
|
|
|
from django.http import HttpResponse
|
2010-03-24 04:05:50 +03:00
|
|
|
from django.shortcuts import get_object_or_404
|
2010-02-10 22:35:25 +03:00
|
|
|
|
2010-04-14 09:15:16 +04:00
|
|
|
from tower import ugettext as _, ugettext_lazy as _lazy
|
2010-02-06 01:06:16 +03:00
|
|
|
import jingo
|
|
|
|
import product_details
|
|
|
|
|
2010-02-10 22:35:25 +03:00
|
|
|
import amo.utils
|
|
|
|
from addons.models import Addon, Category
|
2010-04-01 00:55:24 +04:00
|
|
|
from addons.views import HomepageFilter
|
2010-02-10 22:35:25 +03:00
|
|
|
from translations.query import order_by_translation
|
2010-02-06 01:06:16 +03:00
|
|
|
|
|
|
|
|
|
|
|
languages = dict((lang.lower(), val)
|
|
|
|
for lang, val in product_details.languages.items())
|
|
|
|
|
|
|
|
|
|
|
|
def locale_display_name(locale):
|
|
|
|
"""
|
|
|
|
Return (english name, native name) for the locale.
|
|
|
|
|
|
|
|
Raises KeyError if the locale can't be found.
|
|
|
|
"""
|
|
|
|
if not locale:
|
|
|
|
raise KeyError
|
|
|
|
|
|
|
|
if locale.lower() in languages:
|
|
|
|
v = languages[locale.lower()]
|
|
|
|
return v['English'], v['native']
|
|
|
|
else:
|
|
|
|
# Take out the regional portion and try again.
|
|
|
|
hyphen = locale.rfind('-')
|
|
|
|
if hyphen == -1:
|
|
|
|
raise KeyError
|
|
|
|
else:
|
|
|
|
return locale_display_name(locale[:hyphen])
|
|
|
|
|
|
|
|
|
|
|
|
Locale = collections.namedtuple('Locale', 'locale display native dicts packs')
|
|
|
|
|
|
|
|
|
|
|
|
def language_tools(request):
|
|
|
|
types = (amo.ADDON_DICT, amo.ADDON_LPAPP)
|
2010-02-17 02:22:39 +03:00
|
|
|
q = (Addon.objects.public().exclude(target_locale='')
|
|
|
|
.filter(type__in=types, target_locale__isnull=False))
|
2010-02-06 01:06:16 +03:00
|
|
|
addons = [a for a in q.all() if request.APP in a.compatible_apps]
|
|
|
|
|
|
|
|
for addon in addons:
|
|
|
|
locale = addon.target_locale.lower()
|
|
|
|
try:
|
|
|
|
english, native = locale_display_name(locale)
|
|
|
|
# Add the locale as a differentiator if we had to strip the
|
|
|
|
# regional portion.
|
|
|
|
if locale not in languages:
|
|
|
|
native = '%s (%s)' % (native, locale)
|
|
|
|
addon.locale_display, addon.locale_native = english, native
|
|
|
|
except KeyError:
|
|
|
|
english = u'%s (%s)' % (addon.name, locale)
|
|
|
|
addon.locale_display, addon.locale_native = english, ''
|
|
|
|
|
|
|
|
locales = {}
|
|
|
|
for locale, addons in itertools.groupby(addons, lambda x: x.target_locale):
|
|
|
|
addons = list(addons)
|
|
|
|
dicts = [a for a in addons if a.type_id == amo.ADDON_DICT]
|
|
|
|
packs = [a for a in addons if a.type_id == amo.ADDON_LPAPP]
|
|
|
|
addon = addons[0]
|
|
|
|
locales[locale] = Locale(addon.target_locale, addon.locale_display,
|
|
|
|
addon.locale_native, dicts, packs)
|
|
|
|
|
|
|
|
locales = sorted(locales.items(), key=lambda x: x[1].display)
|
2010-05-17 18:34:35 +04:00
|
|
|
|
|
|
|
search_cat = '%s,0' % amo.ADDON_DICT
|
|
|
|
|
2010-02-06 01:06:16 +03:00
|
|
|
return jingo.render(request, 'browse/language_tools.html',
|
2010-05-17 18:34:35 +04:00
|
|
|
{'locales': locales, 'search_cat': search_cat})
|
2010-02-10 22:35:25 +03:00
|
|
|
|
|
|
|
# Placeholder for the All category.
|
|
|
|
_Category = collections.namedtuple('Category', 'name count slug')
|
|
|
|
|
|
|
|
|
2010-03-24 04:04:57 +03:00
|
|
|
class AddonFilter(object):
|
2010-02-10 22:35:25 +03:00
|
|
|
"""
|
|
|
|
Support class for sorting add-ons. Sortable fields are defined as
|
|
|
|
(value, title) pairs in ``opts``. Pass in a request and a queryset and
|
2010-03-24 04:04:57 +03:00
|
|
|
AddonFilter will figure out how to sort the queryset.
|
2010-02-10 22:35:25 +03:00
|
|
|
|
|
|
|
self.sorting: the field we're sorting by
|
|
|
|
self.opts: all the sort options
|
|
|
|
self.qs: the sorted queryset
|
|
|
|
"""
|
2010-03-04 21:41:07 +03:00
|
|
|
opts = (('name', _lazy(u'Name')),
|
2010-03-24 04:03:47 +03:00
|
|
|
('updated', _lazy(u'Updated')),
|
|
|
|
('created', _lazy(u'Created')),
|
2010-03-04 21:41:07 +03:00
|
|
|
('downloads', _lazy(u'Downloads')),
|
|
|
|
('rating', _lazy(u'Rating')))
|
2010-02-10 22:35:25 +03:00
|
|
|
|
|
|
|
def __init__(self, request, queryset, default):
|
|
|
|
self.sorting = self.options(request, default)
|
|
|
|
self.qs = self.sort(queryset, self.sorting)
|
|
|
|
|
|
|
|
def __iter__(self):
|
|
|
|
"""Cleverness: this lets you unpack the class like a tuple."""
|
|
|
|
return iter((self.sorting, self.opts, self.qs))
|
|
|
|
|
|
|
|
def options(self, request, default):
|
|
|
|
opts_dict = dict(self.opts)
|
|
|
|
if 'sort' in request.GET and request.GET['sort'] in opts_dict:
|
|
|
|
sort = request.GET['sort']
|
|
|
|
return sort
|
|
|
|
else:
|
|
|
|
return default
|
|
|
|
|
|
|
|
def sort(self, qs, field):
|
2010-03-24 04:03:47 +03:00
|
|
|
if field == 'updated':
|
2010-02-18 01:05:02 +03:00
|
|
|
return qs.order_by('-last_updated')
|
2010-03-24 04:03:47 +03:00
|
|
|
if field == 'created':
|
|
|
|
return qs.order_by('-created')
|
2010-02-10 22:35:25 +03:00
|
|
|
elif field == 'downloads':
|
|
|
|
return qs.order_by('-weekly_downloads')
|
|
|
|
elif field == 'rating':
|
|
|
|
return qs.order_by('-bayesian_rating')
|
|
|
|
else:
|
|
|
|
return order_by_translation(qs, 'name')
|
|
|
|
|
|
|
|
|
|
|
|
def themes(request, category=None):
|
2010-03-24 04:04:57 +03:00
|
|
|
q = Category.objects.filter(application=request.APP.id,
|
|
|
|
type=amo.ADDON_THEME)
|
2010-02-10 22:35:25 +03:00
|
|
|
categories = order_by_translation(q, 'name')
|
|
|
|
|
2010-04-14 08:22:29 +04:00
|
|
|
addons, filter, unreviewed = _listing(request, amo.ADDON_THEME)
|
2010-02-10 22:35:25 +03:00
|
|
|
total_count = addons.count()
|
|
|
|
|
|
|
|
if category is None:
|
|
|
|
selected = _Category(_('All'), total_count, '')
|
|
|
|
else:
|
|
|
|
selected = dict((c.slug, c) for c in categories)[category]
|
|
|
|
addons = addons.filter(categories__slug=category)
|
|
|
|
|
|
|
|
themes = amo.utils.paginate(request, addons)
|
|
|
|
|
2010-05-17 18:34:35 +04:00
|
|
|
# Pre-selected category for search form
|
|
|
|
search_cat = '%s,0' % amo.ADDON_THEME
|
|
|
|
|
2010-02-10 22:35:25 +03:00
|
|
|
return jingo.render(request, 'browse/themes.html',
|
|
|
|
{'categories': categories, 'total_count': total_count,
|
|
|
|
'themes': themes, 'selected': selected,
|
2010-03-24 04:04:57 +03:00
|
|
|
'sorting': filter.sorting,
|
|
|
|
'sort_opts': filter.opts,
|
2010-05-17 18:34:35 +04:00
|
|
|
'unreviewed': unreviewed,
|
|
|
|
'search_cat': search_cat})
|
2010-03-24 04:04:57 +03:00
|
|
|
|
|
|
|
|
|
|
|
def _listing(request, addon_type, default='downloads'):
|
|
|
|
# Set up the queryset and filtering for themes & extension listing pages.
|
|
|
|
status = [amo.STATUS_PUBLIC]
|
|
|
|
|
2010-04-14 08:22:29 +04:00
|
|
|
unreviewed = 'on' if request.GET.get('unreviewed', False) else None
|
|
|
|
if unreviewed:
|
|
|
|
status.append(amo.STATUS_UNREVIEWED)
|
2010-03-24 04:04:57 +03:00
|
|
|
|
|
|
|
qs = (Addon.objects.listed(request.APP, *status)
|
|
|
|
.filter(type=addon_type).distinct())
|
|
|
|
filter = AddonFilter(request, qs, default)
|
2010-04-14 08:22:29 +04:00
|
|
|
return filter.qs, filter, unreviewed
|
2010-03-24 04:05:50 +03:00
|
|
|
|
|
|
|
|
|
|
|
def extensions(request, category=None):
|
|
|
|
TYPE = amo.ADDON_EXTENSION
|
|
|
|
|
|
|
|
if category is not None:
|
|
|
|
q = Category.objects.filter(application=request.APP.id, type=TYPE)
|
|
|
|
category = get_object_or_404(q, slug=category)
|
2010-04-01 00:55:24 +04:00
|
|
|
|
|
|
|
if 'sort' not in request.GET and category:
|
|
|
|
return category_landing(request, category)
|
|
|
|
|
2010-04-14 08:22:29 +04:00
|
|
|
addons, filter, unreviewed = _listing(request, TYPE)
|
2010-04-01 00:55:24 +04:00
|
|
|
|
|
|
|
if category:
|
2010-03-24 04:05:50 +03:00
|
|
|
addons = addons.filter(categories__id=category.id)
|
|
|
|
|
|
|
|
addons = amo.utils.paginate(request, addons)
|
|
|
|
|
2010-05-17 18:34:35 +04:00
|
|
|
search_cat = '%s,%s' % (TYPE, category.id if category else 0)
|
|
|
|
|
2010-03-24 04:05:50 +03:00
|
|
|
return jingo.render(request, 'browse/extensions.html',
|
|
|
|
{'category': category, 'addons': addons,
|
2010-04-14 08:22:29 +04:00
|
|
|
'unreviewed': unreviewed,
|
2010-03-24 04:05:50 +03:00
|
|
|
'sorting': filter.sorting,
|
2010-05-17 18:34:35 +04:00
|
|
|
'sort_opts': filter.opts,
|
|
|
|
'search_cat': search_cat})
|
2010-04-01 00:55:24 +04:00
|
|
|
|
|
|
|
|
|
|
|
class CategoryLandingFilter(HomepageFilter):
|
|
|
|
|
2010-04-23 07:34:54 +04:00
|
|
|
opts = (('featured', _lazy('Featured')),
|
|
|
|
('created', _lazy('Recently Added')),
|
|
|
|
('downloads', _lazy('Top Downloads')),
|
|
|
|
('rating', _lazy('Top Rated')))
|
2010-04-01 00:55:24 +04:00
|
|
|
|
2010-04-09 05:42:02 +04:00
|
|
|
def __init__(self, request, base, category, key, default):
|
|
|
|
self.category = category
|
|
|
|
super(CategoryLandingFilter, self).__init__(request, base, key,
|
|
|
|
default)
|
|
|
|
|
2010-04-01 00:55:24 +04:00
|
|
|
def _filter(self, field):
|
|
|
|
qs = Addon.objects
|
|
|
|
if field == 'created':
|
|
|
|
return qs.order_by('-created')
|
|
|
|
elif field == 'downloads':
|
|
|
|
return qs.order_by('-weekly_downloads')
|
|
|
|
elif field == 'rating':
|
|
|
|
return qs.order_by('-bayesian_rating')
|
|
|
|
else:
|
2010-04-09 05:42:02 +04:00
|
|
|
return qs.filter(addoncategory__feature=True,
|
|
|
|
addoncategory__category=self.category)
|
2010-04-01 00:55:24 +04:00
|
|
|
|
|
|
|
|
|
|
|
def category_landing(request, category):
|
|
|
|
base = (Addon.objects.listed(request.APP).exclude(type=amo.ADDON_PERSONA)
|
|
|
|
.filter(categories__id=category.id))
|
2010-04-09 05:42:02 +04:00
|
|
|
filter = CategoryLandingFilter(request, base, category,
|
2010-04-01 00:55:24 +04:00
|
|
|
key='browse', default='featured')
|
|
|
|
|
2010-05-17 18:34:35 +04:00
|
|
|
search_cat = '%s,%s' % (category.type_id, category.id)
|
|
|
|
|
2010-04-01 00:55:24 +04:00
|
|
|
return jingo.render(request, 'browse/category_landing.html',
|
2010-05-17 18:34:35 +04:00
|
|
|
{'category': category, 'filter': filter,
|
|
|
|
'search_cat': search_cat})
|
2010-04-01 03:46:30 +04:00
|
|
|
|
|
|
|
|
|
|
|
def creatured(request, category):
|
|
|
|
TYPE = amo.ADDON_EXTENSION
|
|
|
|
q = Category.objects.filter(application=request.APP.id, type=TYPE)
|
|
|
|
category = get_object_or_404(q, slug=category)
|
|
|
|
addons = Addon.objects.filter(addoncategory__feature=True,
|
|
|
|
addoncategory__category=category)
|
|
|
|
return jingo.render(request, 'browse/creatured.html',
|
|
|
|
{'addons': addons, 'category': category})
|
2010-04-02 19:43:45 +04:00
|
|
|
|
|
|
|
|
|
|
|
class PersonasFilter(HomepageFilter):
|
|
|
|
|
2010-04-23 07:34:54 +04:00
|
|
|
opts = (('up-and-coming', _lazy('Up & Coming')),
|
|
|
|
('created', _lazy('Recently Added')),
|
|
|
|
('popular', _lazy('Most Popular')),
|
|
|
|
('rating', _lazy('Top Rated')))
|
2010-04-02 19:43:45 +04:00
|
|
|
|
|
|
|
def _filter(self, field):
|
|
|
|
qs = Addon.objects
|
|
|
|
if field == 'created':
|
|
|
|
return qs.order_by('-created')
|
|
|
|
elif field == 'popular':
|
|
|
|
return qs.order_by('-persona__popularity')
|
|
|
|
elif field == 'rating':
|
|
|
|
return qs.order_by('-bayesian_rating')
|
|
|
|
else:
|
|
|
|
return qs.order_by('-persona__movers')
|
|
|
|
|
|
|
|
|
|
|
|
def personas(request, category=None):
|
|
|
|
TYPE = amo.ADDON_PERSONA
|
|
|
|
q = Category.objects.filter(application=request.APP.id,
|
|
|
|
type=TYPE)
|
|
|
|
categories = order_by_translation(q, 'name')
|
|
|
|
|
2010-04-07 21:40:56 +04:00
|
|
|
base = Addon.objects.valid().filter(type=TYPE)
|
|
|
|
|
2010-04-02 19:43:45 +04:00
|
|
|
if category is not None:
|
|
|
|
category = get_object_or_404(q, slug=category)
|
2010-04-07 21:40:56 +04:00
|
|
|
base = base.filter(categories__id=category.id)
|
2010-04-02 19:43:45 +04:00
|
|
|
|
|
|
|
filter = PersonasFilter(request, base, key='sort', default='up-and-coming')
|
|
|
|
|
2010-04-07 21:40:56 +04:00
|
|
|
if 'sort' in request.GET:
|
|
|
|
template = 'grid.html'
|
|
|
|
else:
|
|
|
|
template = 'category_landing.html'
|
|
|
|
|
|
|
|
addons = amo.utils.paginate(request, filter.qs, 30)
|
2010-05-17 18:34:35 +04:00
|
|
|
|
|
|
|
search_cat = '%s,%s' % (TYPE, category.id if category else 0)
|
|
|
|
|
2010-04-07 21:40:56 +04:00
|
|
|
return jingo.render(request, 'browse/personas/' + template,
|
2010-04-02 19:43:45 +04:00
|
|
|
{'categories': categories, 'category': category,
|
2010-05-17 18:34:35 +04:00
|
|
|
'filter': filter, 'addons': addons,
|
|
|
|
'search_cat': search_cat})
|
2010-04-19 21:06:59 +04:00
|
|
|
|
|
|
|
|
|
|
|
def search_providers(request, category=None):
|
|
|
|
return HttpResponse("Search providers browse page stub.")
|