Merge branch 'blocklist'
This commit is contained in:
Коммит
e917639574
|
@ -351,3 +351,9 @@ def media(context, url):
|
|||
else:
|
||||
build = context['BUILD_ID_IMG']
|
||||
return context['MEDIA_URL'] + utils.urlparams(url, b=build)
|
||||
|
||||
|
||||
@register.function
|
||||
@jinja2.evalcontextfunction
|
||||
def attrs(ctx, *args, **kw):
|
||||
return jinja2.filters.do_xmlattr(ctx, dict(*args, **kw))
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
from django.contrib import admin
|
||||
from .models import BlocklistApp, BlocklistItem, BlocklistPlugin
|
||||
from .models import BlocklistApp, BlocklistItem, BlocklistPlugin, BlocklistGfx
|
||||
|
||||
admin.site.register(BlocklistApp)
|
||||
admin.site.register(BlocklistItem)
|
||||
admin.site.register(BlocklistPlugin)
|
||||
admin.site.register(BlocklistGfx)
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
from django.db import models
|
||||
|
||||
import redisutils
|
||||
|
||||
import amo.models
|
||||
|
||||
|
||||
class BlocklistApp(amo.models.ModelBase):
|
||||
blitem = models.ForeignKey('BlocklistItem')
|
||||
blitem = models.ForeignKey('BlocklistItem', related_name='app')
|
||||
guid = models.CharField(max_length=255, blank=True, db_index=True,
|
||||
null=True)
|
||||
min = models.CharField(max_length=255, blank=True, null=True)
|
||||
|
@ -57,3 +59,25 @@ class BlocklistPlugin(amo.models.ModelBase):
|
|||
|
||||
def flush_urls(self):
|
||||
return ['/blocklist*'] # no lang/app
|
||||
|
||||
|
||||
class BlocklistGfx(amo.models.ModelBase):
|
||||
guid = models.CharField(max_length=255, blank=True, null=True)
|
||||
os = models.CharField(max_length=255, blank=True, null=True)
|
||||
vendor = models.CharField(max_length=255, blank=True, null=True)
|
||||
devices = models.CharField(max_length=255, blank=True, null=True)
|
||||
feature = models.CharField(max_length=255, blank=True, null=True)
|
||||
feature_status = models.CharField(max_length=255, blank=True, null=True)
|
||||
driver_version = models.CharField(max_length=255, blank=True, null=True)
|
||||
driver_version_comparator = models.CharField(max_length=255, blank=True,
|
||||
null=True)
|
||||
|
||||
class Meta:
|
||||
db_table = 'blgfxdrivers'
|
||||
|
||||
def __unicode__(self):
|
||||
return '%s: %s : %s : %s' % (self.guid, self.os, self.vendor,
|
||||
self.devices)
|
||||
|
||||
def flush_urls(self):
|
||||
return ['/blocklist*'] # no lang/app
|
||||
|
|
|
@ -0,0 +1,68 @@
|
|||
<?xml version="1.0"?>
|
||||
<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist">
|
||||
{% if items %}
|
||||
<emItems>
|
||||
{% for guid, rows in items.items() %}
|
||||
<emItem id="{{ guid }}" {{ attrs(os=rows.os) }}>
|
||||
{% for row in rows.rows %}
|
||||
{% if row.min or row.max or row.severity or row.apps %}
|
||||
<versionRange {{ attrs(minVersion=row.min, maxVersion=row.max,
|
||||
severity=row.severity) }}>
|
||||
{% for app in row.apps %}
|
||||
<targetApplication {{ attrs(id=app.guid) }}>
|
||||
{% if app.min and app.max %}
|
||||
<versionRange {{ attrs(minVersion=app.min, maxVersion=app.max) }} />
|
||||
{% endif %}
|
||||
</targetApplication>
|
||||
{% endfor %}
|
||||
</versionRange>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</emItem>
|
||||
{% endfor %}
|
||||
</emItems>
|
||||
{% endif %}
|
||||
|
||||
{% if plugins %}
|
||||
<pluginItems>
|
||||
{% for plugin in plugins %}
|
||||
<pluginItem {{ attrs(os=plugin.os, xpcomabi=plugin.xpcomabi) }}>
|
||||
{% if plugin.name %}<match name="name" exp="{{ plugin.name }}" />{% endif %}
|
||||
{% if plugin.description %}<match name="description" exp="{{ plugin.description }}" />{% endif %}
|
||||
{% if plugin.filename %}<match name="filename" exp="{{ plugin.filename }}" />{% endif %}
|
||||
{% if plugin.severity or plugin.min or plugin.max %}
|
||||
<versionRange {{ attrs(severity=plugin.severity) }}>
|
||||
{% if apiver > 2 and plugin.min and plugin.max %}
|
||||
<targetApplication id="{{ appguid }}">
|
||||
<versionRange {{ attrs(minVersion=plugin.min, maxVersion=plugin.max) }} />
|
||||
</targetApplication>
|
||||
{% endif %}
|
||||
</versionRange>
|
||||
{% endif %}
|
||||
</pluginItem>
|
||||
{% endfor %}
|
||||
</pluginItems>
|
||||
{% endif %}
|
||||
|
||||
{% if gfxs %}
|
||||
<gfxItems>
|
||||
{% for gfx in gfxs %}
|
||||
<gfxBlacklistEntry>
|
||||
<os>{{ gfx.os }}</os>
|
||||
<vendor>{{ gfx.vendor }}</vendor>
|
||||
<devices>
|
||||
{% for device in gfx.devices.split(' ') %}
|
||||
<device>{{ device }}</device>
|
||||
{% endfor %}
|
||||
</devices>
|
||||
<feature>{{ gfx.feature }}</feature>
|
||||
<featureStatus>{{ gfx.feature_status }}</featureStatus>
|
||||
<driverVersion>{{ gfx.driver_version }}</driverVersion>
|
||||
<driverVersionComparator>{{ gfx.driver_version_comparator }}</driverVersionComparator>
|
||||
</gfxBlacklistEntry>
|
||||
{% endfor %}
|
||||
</gfxItems>
|
||||
{% endif %}
|
||||
|
||||
|
||||
</blocklist>
|
|
@ -0,0 +1,308 @@
|
|||
from xml.dom import minidom
|
||||
|
||||
from django.conf import settings
|
||||
from django.core.cache import cache
|
||||
|
||||
import redisutils
|
||||
import test_utils
|
||||
from nose.tools import eq_
|
||||
|
||||
import amo
|
||||
from amo.urlresolvers import reverse
|
||||
from .models import BlocklistItem, BlocklistApp, BlocklistPlugin, BlocklistGfx
|
||||
|
||||
|
||||
base_xml = """
|
||||
<?xml version="1.0"?>
|
||||
<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist">
|
||||
</blocklist>
|
||||
"""
|
||||
|
||||
|
||||
class BlocklistTest(test_utils.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.fx4_url = reverse('blocklist', args=[3, amo.FIREFOX.guid, '4.0'])
|
||||
self.fx2_url = reverse('blocklist', args=[2, amo.FIREFOX.guid, '2.0'])
|
||||
self.mobile_url = reverse('blocklist', args=[2, amo.MOBILE.guid, '.9'])
|
||||
self._redis = redisutils.mock_redis()
|
||||
|
||||
def tearDown(self):
|
||||
redisutils.reset_redis(self._redis)
|
||||
|
||||
def normalize(self, s):
|
||||
return '\n'.join(x.strip() for x in s.split())
|
||||
|
||||
def eq_(self, x, y):
|
||||
return eq_(self.normalize(x), self.normalize(y))
|
||||
|
||||
|
||||
class BlocklistItemTest(BlocklistTest):
|
||||
|
||||
def setUp(self):
|
||||
super(BlocklistItemTest, self).setUp()
|
||||
self.item = BlocklistItem.objects.create(guid='guid@addon.com')
|
||||
self.app = BlocklistApp.objects.create(blitem=self.item,
|
||||
guid=amo.FIREFOX.guid)
|
||||
|
||||
def test_no_items(self):
|
||||
self.item.delete()
|
||||
r = self.client.get(self.fx4_url)
|
||||
self.eq_(r.content, base_xml)
|
||||
|
||||
def test_new_cookie_per_user(self):
|
||||
self.client.get(self.fx4_url)
|
||||
assert settings.BLOCKLIST_COOKIE in self.client.cookies
|
||||
c = self.client.cookies[settings.BLOCKLIST_COOKIE]
|
||||
eq_(c['path'], '/blocklist/')
|
||||
eq_(c['secure'], True)
|
||||
|
||||
def test_existing_user_cookie(self):
|
||||
self.client.cookies[settings.BLOCKLIST_COOKIE] = 'adfadf'
|
||||
self.client.get(self.fx4_url)
|
||||
eq_(self.client.cookies[settings.BLOCKLIST_COOKIE].value, 'adfadf')
|
||||
|
||||
def test_url_params(self):
|
||||
eq_(self.client.get(self.fx4_url).status_code, 200)
|
||||
eq_(self.client.get(self.fx2_url).status_code, 200)
|
||||
# We ignore trailing url parameters.
|
||||
eq_(self.client.get(self.fx4_url + 'other/junk/').status_code, 200)
|
||||
|
||||
def test_app_guid(self):
|
||||
# There's one item for Firefox.
|
||||
r = self.client.get(self.fx4_url)
|
||||
eq_(r.status_code, 200)
|
||||
eq_(len(r.context['items']), 1)
|
||||
|
||||
# There are no items for mobile.
|
||||
r = self.client.get(self.mobile_url)
|
||||
eq_(r.status_code, 200)
|
||||
eq_(len(r.context['items']), 0)
|
||||
|
||||
# Without the app constraint we see the item.
|
||||
self.app.delete()
|
||||
r = self.client.get(self.mobile_url)
|
||||
eq_(r.status_code, 200)
|
||||
eq_(len(r.context['items']), 1)
|
||||
|
||||
def dom(self, url):
|
||||
r = self.client.get(self.fx4_url)
|
||||
return minidom.parseString(r.content)
|
||||
|
||||
def test_item_guid(self):
|
||||
items = self.dom(self.fx4_url).getElementsByTagName('emItem')
|
||||
eq_(len(items), 1)
|
||||
eq_(items[0].getAttribute('id'), 'guid@addon.com')
|
||||
|
||||
def test_item_os(self):
|
||||
item = self.dom(self.fx4_url).getElementsByTagName('emItem')[0]
|
||||
assert 'os' not in item.attributes.keys()
|
||||
|
||||
self.item.update(os='win,mac')
|
||||
item = self.dom(self.fx4_url).getElementsByTagName('emItem')[0]
|
||||
eq_(item.getAttribute('os'), 'win,mac')
|
||||
|
||||
def test_item_severity(self):
|
||||
self.item.update(severity=2)
|
||||
eq_(len(self.vr()), 1)
|
||||
item = self.dom(self.fx4_url).getElementsByTagName('emItem')[0]
|
||||
vrange = item.getElementsByTagName('versionRange')
|
||||
eq_(vrange[0].getAttribute('severity'), '2')
|
||||
|
||||
def vr(self):
|
||||
item = self.dom(self.fx4_url).getElementsByTagName('emItem')[0]
|
||||
return item.getElementsByTagName('versionRange')
|
||||
|
||||
def test_item_version_range(self):
|
||||
self.item.update(min='0.1')
|
||||
eq_(len(self.vr()), 1)
|
||||
eq_(self.vr()[0].attributes.keys(), ['minVersion'])
|
||||
eq_(self.vr()[0].getAttribute('minVersion'), '0.1')
|
||||
|
||||
self.item.update(max='0.2')
|
||||
eq_(self.vr()[0].attributes.keys(), ['minVersion', 'maxVersion'])
|
||||
eq_(self.vr()[0].getAttribute('minVersion'), '0.1')
|
||||
eq_(self.vr()[0].getAttribute('maxVersion'), '0.2')
|
||||
|
||||
def test_item_multiple_version_range(self):
|
||||
# There should be two <versionRange>s under one <emItem>.
|
||||
self.item.update(min='0.1', max='0.2')
|
||||
BlocklistItem.objects.create(guid=self.item.guid, severity=3)
|
||||
|
||||
item = self.dom(self.fx4_url).getElementsByTagName('emItem')
|
||||
eq_(len(item), 1)
|
||||
vr = item[0].getElementsByTagName('versionRange')
|
||||
eq_(len(vr), 2)
|
||||
eq_(vr[0].getAttribute('minVersion'), '0.1')
|
||||
eq_(vr[0].getAttribute('maxVersion'), '0.2')
|
||||
eq_(vr[1].getAttribute('severity'), '3')
|
||||
|
||||
def test_item_target_app(self):
|
||||
app = self.app
|
||||
self.app.delete()
|
||||
self.item.update(severity=2)
|
||||
version_range = self.vr()[0]
|
||||
eq_(version_range.getElementsByTagName('targetApplication'), [])
|
||||
|
||||
app.save()
|
||||
version_range = self.vr()[0]
|
||||
target_app = version_range.getElementsByTagName('targetApplication')
|
||||
eq_(len(target_app), 1)
|
||||
eq_(target_app[0].getAttribute('id'), amo.FIREFOX.guid)
|
||||
|
||||
app.update(min='0.1', max='*')
|
||||
version_range = self.vr()[0]
|
||||
target_app = version_range.getElementsByTagName('targetApplication')
|
||||
eq_(target_app[0].getAttribute('id'), amo.FIREFOX.guid)
|
||||
tvr = target_app[0].getElementsByTagName('versionRange')
|
||||
eq_(tvr[0].getAttribute('minVersion'), '0.1')
|
||||
eq_(tvr[0].getAttribute('maxVersion'), '*')
|
||||
|
||||
def test_item_multiple_apps(self):
|
||||
# Make sure all <targetApplication>s go under the same <versionRange>.
|
||||
self.app.update(min='0.1', max='0.2')
|
||||
BlocklistApp.objects.create(guid=amo.FIREFOX.guid, blitem=self.item,
|
||||
min='3.0', max='3.1')
|
||||
version_range = self.vr()[0]
|
||||
apps = version_range.getElementsByTagName('targetApplication')
|
||||
eq_(len(apps), 2)
|
||||
eq_(apps[0].getAttribute('id'), amo.FIREFOX.guid)
|
||||
vr = apps[0].getElementsByTagName('versionRange')[0]
|
||||
eq_(vr.getAttribute('minVersion'), '0.1')
|
||||
eq_(vr.getAttribute('maxVersion'), '0.2')
|
||||
eq_(apps[1].getAttribute('id'), amo.FIREFOX.guid)
|
||||
vr = apps[1].getElementsByTagName('versionRange')[0]
|
||||
eq_(vr.getAttribute('minVersion'), '3.0')
|
||||
eq_(vr.getAttribute('maxVersion'), '3.1')
|
||||
|
||||
def test_item_empty_version_range(self):
|
||||
# No version_range without an app, min, max, or severity.
|
||||
self.app.delete()
|
||||
self.item.update(min=None, max=None, severity=None)
|
||||
eq_(len(self.vr()), 0)
|
||||
|
||||
def test_item_empty_target_app(self):
|
||||
# No empty <targetApplication>.
|
||||
self.item.update(severity=1)
|
||||
self.app.delete()
|
||||
eq_(self.dom(self.fx4_url).getElementsByTagName('targetApplication'),
|
||||
[])
|
||||
|
||||
def test_item_target_empty_version_range(self):
|
||||
app = self.dom(self.fx4_url).getElementsByTagName('targetApplication')
|
||||
eq_(app[0].getElementsByTagName('versionRange'), [])
|
||||
|
||||
|
||||
class BlocklistPluginTest(BlocklistTest):
|
||||
|
||||
def setUp(self):
|
||||
super(BlocklistPluginTest, self).setUp()
|
||||
self.plugin = BlocklistPlugin.objects.create(guid=amo.FIREFOX.guid)
|
||||
|
||||
def test_no_plugins(self):
|
||||
r = self.client.get(self.mobile_url)
|
||||
self.eq_(r.content, base_xml)
|
||||
|
||||
def dom(self, url=None):
|
||||
url = url or self.fx4_url
|
||||
r = self.client.get(url)
|
||||
d = minidom.parseString(r.content)
|
||||
return d.getElementsByTagName('pluginItem')[0]
|
||||
|
||||
def test_plugin_empty(self):
|
||||
eq_(self.dom().attributes.keys(), [])
|
||||
eq_(self.dom().getElementsByTagName('match'), [])
|
||||
eq_(self.dom().getElementsByTagName('versionRange'), [])
|
||||
|
||||
def test_plugin_os(self):
|
||||
self.plugin.update(os='win')
|
||||
eq_(self.dom().attributes.keys(), ['os'])
|
||||
eq_(self.dom().getAttribute('os'), 'win')
|
||||
|
||||
def test_plugin_xpcomabi(self):
|
||||
self.plugin.update(xpcomabi='win')
|
||||
eq_(self.dom().attributes.keys(), ['xpcomabi'])
|
||||
eq_(self.dom().getAttribute('xpcomabi'), 'win')
|
||||
|
||||
def test_plugin_name(self):
|
||||
self.plugin.update(name='flash')
|
||||
match = self.dom().getElementsByTagName('match')
|
||||
eq_(len(match), 1)
|
||||
eq_(dict(match[0].attributes.items()),
|
||||
{'name': 'name', 'exp': 'flash'})
|
||||
|
||||
def test_plugin_description(self):
|
||||
self.plugin.update(description='flash')
|
||||
match = self.dom().getElementsByTagName('match')
|
||||
eq_(len(match), 1)
|
||||
eq_(dict(match[0].attributes.items()),
|
||||
{'name': 'description', 'exp': 'flash'})
|
||||
|
||||
def test_plugin_filename(self):
|
||||
self.plugin.update(filename='flash')
|
||||
match = self.dom().getElementsByTagName('match')
|
||||
eq_(len(match), 1)
|
||||
eq_(dict(match[0].attributes.items()),
|
||||
{'name': 'filename', 'exp': 'flash'})
|
||||
|
||||
def test_plugin_severity(self):
|
||||
self.plugin.update(severity=2)
|
||||
v = self.dom().getElementsByTagName('versionRange')[0]
|
||||
eq_(v.getAttribute('severity'), '2')
|
||||
|
||||
def test_plugin_target_app(self):
|
||||
self.plugin.update(min='1', max='2')
|
||||
v = self.dom().getElementsByTagName('versionRange')[0]
|
||||
app = v.getElementsByTagName('targetApplication')[0]
|
||||
eq_(app.getAttribute('id'), amo.FIREFOX.guid)
|
||||
vr = app.getElementsByTagName('versionRange')[0]
|
||||
eq_(vr.getAttribute('minVersion'), '1')
|
||||
eq_(vr.getAttribute('maxVersion'), '2')
|
||||
|
||||
def test_plugin_apiver_lt_3(self):
|
||||
self.plugin.update(severity='2')
|
||||
# No min & max so the app matches.
|
||||
e = self.dom(self.fx2_url).getElementsByTagName('versionRange')[0]
|
||||
eq_(e.getAttribute('severity'), '2')
|
||||
eq_(e.getElementsByTagName('targetApplication'), [])
|
||||
|
||||
# The app version is not in range.
|
||||
self.plugin.update(min='3.0', max='4.0')
|
||||
self.assertRaises(IndexError, self.dom, self.fx2_url)
|
||||
|
||||
# The app is back in range.
|
||||
self.plugin.update(min='1.1')
|
||||
e = self.dom(self.fx2_url).getElementsByTagName('versionRange')[0]
|
||||
eq_(e.getAttribute('severity'), '2')
|
||||
eq_(e.getElementsByTagName('targetApplication'), [])
|
||||
|
||||
|
||||
class BlocklistGfxTest(BlocklistTest):
|
||||
|
||||
def setUp(self):
|
||||
super(BlocklistGfxTest, self).setUp()
|
||||
self.gfx = BlocklistGfx.objects.create(
|
||||
guid=amo.FIREFOX.guid, os='os', vendor='vendor', devices='x y z',
|
||||
feature='feature', feature_status='status',
|
||||
driver_version='version', driver_version_comparator='compare')
|
||||
|
||||
def test_no_gfx(self):
|
||||
r = self.client.get(self.mobile_url)
|
||||
self.eq_(r.content, base_xml)
|
||||
|
||||
def test_gfx(self):
|
||||
r = self.client.get(self.fx4_url)
|
||||
dom = minidom.parseString(r.content)
|
||||
gfx = dom.getElementsByTagName('gfxBlacklistEntry')[0]
|
||||
find = lambda e: gfx.getElementsByTagName(e)[0].childNodes[0].wholeText
|
||||
eq_(find('os'), self.gfx.os)
|
||||
eq_(find('feature'), self.gfx.feature)
|
||||
eq_(find('vendor'), self.gfx.vendor)
|
||||
eq_(find('featureStatus'), self.gfx.feature_status)
|
||||
eq_(find('driverVersion'), self.gfx.driver_version)
|
||||
eq_(find('driverVersionComparator'),
|
||||
self.gfx.driver_version_comparator)
|
||||
devices = gfx.getElementsByTagName('devices')[0]
|
||||
for device, val in zip(devices.getElementsByTagName('device'),
|
||||
self.gfx.devices.split(' ')):
|
||||
eq_(device.childNodes[0].wholeText, val)
|
|
@ -0,0 +1,91 @@
|
|||
import collections
|
||||
from datetime import datetime, timedelta
|
||||
import uuid
|
||||
|
||||
from django.core.cache import cache
|
||||
from django.conf import settings
|
||||
from django.db.models import Q, signals as db_signals
|
||||
|
||||
import jingo
|
||||
import redisutils
|
||||
|
||||
from amo.utils import sorted_groupby
|
||||
from versions.compare import version_int
|
||||
from .models import BlocklistItem, BlocklistPlugin, BlocklistGfx, BlocklistApp
|
||||
|
||||
|
||||
App = collections.namedtuple('App', 'guid min max')
|
||||
|
||||
|
||||
def blocklist(request, apiver, app, appver):
|
||||
key = 'blocklist:%s:%s:%s'% (apiver, app, appver)
|
||||
response = cache.get(key)
|
||||
if response is None:
|
||||
response = _blocklist(request, apiver, app, appver)
|
||||
cache.set(key, response, 60 * 60)
|
||||
# This gets cleared with the clear_blocklist signal handler.
|
||||
redisutils.connections['master'].sadd('blocklist:keys', key)
|
||||
if settings.BLOCKLIST_COOKIE not in request.COOKIES:
|
||||
response.set_cookie(settings.BLOCKLIST_COOKIE, uuid.uuid4(),
|
||||
expires=datetime.now() + timedelta(days=5 * 365),
|
||||
path='/blocklist/', secure=True)
|
||||
return response
|
||||
|
||||
|
||||
def _blocklist(request, apiver, app, appver):
|
||||
apiver = int(apiver)
|
||||
items = get_items(apiver, app, appver)
|
||||
plugins = get_plugins(apiver, app, appver)
|
||||
gfxs = BlocklistGfx.objects.filter(Q(guid__isnull=True) | Q(guid=app))
|
||||
return jingo.render(request, 'blocklist/blocklist.xml',
|
||||
dict(items=items, plugins=plugins, gfxs=gfxs,
|
||||
apiver=apiver, appguid=app, appver=appver))
|
||||
|
||||
|
||||
def clear_blocklist(*args, **kw):
|
||||
# Something in the blocklist changed; invalidate all responses.
|
||||
keys = redisutils.connections['master'].smembers('blocklist:keys')
|
||||
cache.delete_many(keys)
|
||||
|
||||
|
||||
for m in BlocklistItem, BlocklistPlugin, BlocklistGfx, BlocklistApp:
|
||||
db_signals.post_save.connect(clear_blocklist, sender=m,
|
||||
dispatch_uid='save_%s' % m)
|
||||
db_signals.post_delete.connect(clear_blocklist, sender=m,
|
||||
dispatch_uid='delete_%s' % m)
|
||||
|
||||
|
||||
def get_items(apiver, app, appver):
|
||||
# Collapse multiple blocklist items (different version ranges) into one
|
||||
# item and collapse each item's apps.
|
||||
addons = (BlocklistItem.uncached
|
||||
.filter(Q(app__guid__isnull=True) | Q(app__guid=app))
|
||||
.extra(select={'app_guid': 'blapps.guid',
|
||||
'app_min': 'blapps.min',
|
||||
'app_max': 'blapps.max'}))
|
||||
items = {}
|
||||
for guid, rows in sorted_groupby(addons, 'guid'):
|
||||
rr = []
|
||||
for id, rs in sorted_groupby(list(rows), 'id'):
|
||||
rs = list(rs)
|
||||
rr.append(rs[0])
|
||||
rs[0].apps = [App(r.app_guid, r.app_min, r.app_max)
|
||||
for r in rs if r.app_guid]
|
||||
os = [r.os for r in rr if r.os]
|
||||
items[guid] = {'rows': rr, 'os': os and os[0] or None}
|
||||
return items
|
||||
|
||||
|
||||
def get_plugins(apiver, app, appver):
|
||||
# API versions < 3 ignore targetApplication entries for plugins so only
|
||||
# block the plugin if the appver is within the block range.
|
||||
plugins = BlocklistPlugin.uncached.filter(
|
||||
Q(guid__isnull=True) | Q(guid=app))
|
||||
if apiver < 3:
|
||||
def between(ver, min, max):
|
||||
if not (min and max):
|
||||
return True
|
||||
return version_int(min) < ver < version_int(max)
|
||||
app_version = version_int(appver)
|
||||
plugins = [p for p in plugins if between(app_version, p.min, p.max)]
|
||||
return plugins
|
|
@ -165,12 +165,12 @@ ADMIN_MEDIA_PREFIX = '/admin-media/'
|
|||
# paths that don't require an app prefix
|
||||
SUPPORTED_NONAPPS = ('admin', 'developers', 'editors', 'img',
|
||||
'jsi18n', 'localizers', 'media', 'robots.txt',
|
||||
'statistics', 'services', 'update')
|
||||
'statistics', 'services', 'update', 'blocklist')
|
||||
DEFAULT_APP = 'firefox'
|
||||
|
||||
# paths that don't require a locale prefix
|
||||
SUPPORTED_NONLOCALES = ('img', 'media', 'robots.txt', 'services', 'downloads',
|
||||
'update')
|
||||
'update', 'blocklist')
|
||||
|
||||
# Make this unique, and don't share it with anybody.
|
||||
SECRET_KEY = 'r#%9w^o_80)7f%!_ir5zx$tu3mupw9u%&s!)-_q%gy7i+fhx#)'
|
||||
|
@ -798,3 +798,5 @@ DEFAULT_SUGGESTED_CONTRIBUTION = 5
|
|||
|
||||
# Path to `ps`.
|
||||
PS_BIN = '/bin/ps'
|
||||
|
||||
BLOCKLIST_COOKIE = 'BLOCKLIST_v1'
|
||||
|
|
6
urls.py
6
urls.py
|
@ -5,6 +5,7 @@ from django.shortcuts import redirect
|
|||
from django.views.i18n import javascript_catalog
|
||||
from django.views.decorators.cache import cache_page
|
||||
|
||||
import blocklist.views
|
||||
import versions.urls
|
||||
|
||||
admin.autodiscover()
|
||||
|
@ -16,6 +17,11 @@ urlpatterns = patterns('',
|
|||
# Discovery pane is first for undetectable efficiency wins.
|
||||
('^discovery/', include('discovery.urls')),
|
||||
|
||||
# There are many more params but we only care about these three. The end is
|
||||
# not anchored on purpose!
|
||||
url('^blocklist/(?P<apiver>\d+)/(?P<app>[^/]+)/(?P<appver>[^/]+)/',
|
||||
blocklist.views.blocklist, name='blocklist'),
|
||||
|
||||
# Add-ons.
|
||||
('', include('addons.urls')),
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче