2011-05-17 02:20:53 +04:00
|
|
|
import csv
|
2010-08-10 21:40:50 +04:00
|
|
|
from urlparse import urlparse
|
|
|
|
|
2010-03-25 01:52:02 +03:00
|
|
|
from django import http
|
2010-06-30 02:28:40 +04:00
|
|
|
# I'm so glad we named a function in here settings...
|
|
|
|
from django.conf import settings as site_settings
|
2010-01-22 04:45:46 +03:00
|
|
|
from django.contrib import admin
|
2011-05-20 02:53:06 +04:00
|
|
|
from django.db.models import Count
|
2011-03-03 05:25:38 +03:00
|
|
|
from django.shortcuts import redirect, get_object_or_404
|
2010-02-24 08:08:24 +03:00
|
|
|
from django.views import debug
|
2009-12-31 01:43:25 +03:00
|
|
|
|
2010-06-30 02:28:40 +04:00
|
|
|
import commonware.log
|
|
|
|
from hera.contrib.django_forms import FlushForm
|
2010-07-23 01:18:37 +04:00
|
|
|
from hera.contrib.django_utils import get_hera, flush_urls
|
2010-03-25 01:52:02 +03:00
|
|
|
import jinja2
|
2009-12-31 01:43:25 +03:00
|
|
|
import jingo
|
2011-05-03 23:31:33 +04:00
|
|
|
from tower import ugettext as _
|
2009-12-31 01:43:25 +03:00
|
|
|
|
2010-09-02 08:07:36 +04:00
|
|
|
from amo import messages
|
2011-05-19 04:19:58 +04:00
|
|
|
from amo import get_user
|
2011-04-27 00:07:59 +04:00
|
|
|
from amo.decorators import login_required, json_view, post_required
|
2010-01-29 04:59:26 +03:00
|
|
|
import amo.models
|
2011-04-27 00:07:59 +04:00
|
|
|
from amo.urlresolvers import reverse
|
2011-05-03 23:31:33 +04:00
|
|
|
from amo.utils import chunked
|
2009-12-31 01:43:25 +03:00
|
|
|
from addons.models import Addon
|
2011-03-03 05:25:38 +03:00
|
|
|
from files.models import Approval, File
|
2009-12-31 01:43:25 +03:00
|
|
|
from versions.models import Version
|
2011-05-20 02:53:06 +04:00
|
|
|
|
|
|
|
from . import tasks
|
|
|
|
from .forms import BulkValidationForm, NotifyForm
|
|
|
|
from .models import ValidationJob, EmailPreviewTopic, Config
|
2009-12-31 01:43:25 +03:00
|
|
|
|
2010-06-30 02:28:40 +04:00
|
|
|
log = commonware.log.getLogger('z.zadmin')
|
|
|
|
|
2009-12-31 01:43:25 +03:00
|
|
|
|
2010-01-22 04:45:46 +03:00
|
|
|
@admin.site.admin_view
|
2009-12-31 01:43:25 +03:00
|
|
|
def flagged(request):
|
2010-02-02 23:22:24 +03:00
|
|
|
addons = Addon.objects.filter(admin_review=True).order_by('-created')
|
2009-12-31 01:43:25 +03:00
|
|
|
|
|
|
|
if request.method == 'POST':
|
|
|
|
ids = map(int, request.POST.getlist('addon_id'))
|
2010-11-17 03:45:22 +03:00
|
|
|
addons = list(addons)
|
2010-02-02 23:22:24 +03:00
|
|
|
Addon.objects.filter(id__in=ids).update(admin_review=False)
|
2009-12-31 01:43:25 +03:00
|
|
|
# The sql update doesn't invalidate anything, do it manually.
|
2010-11-17 03:45:22 +03:00
|
|
|
invalid = [addon for addon in addons if addon.pk in ids]
|
2009-12-31 01:43:25 +03:00
|
|
|
Addon.objects.invalidate(*invalid)
|
2010-04-14 02:59:28 +04:00
|
|
|
return redirect('zadmin.flagged')
|
2009-12-31 01:43:25 +03:00
|
|
|
|
2010-01-29 02:47:01 +03:00
|
|
|
sql = """SELECT {t}.* FROM {t} JOIN (
|
|
|
|
SELECT addon_id, MAX(created) AS created
|
|
|
|
FROM {t}
|
|
|
|
GROUP BY addon_id) as J
|
|
|
|
ON ({t}.addon_id = J.addon_id AND {t}.created = J.created)
|
|
|
|
WHERE {t}.addon_id IN {ids}"""
|
|
|
|
approvals_sql = sql + """
|
|
|
|
AND (({t}.reviewtype = 'nominated' AND {t}.action = %s)
|
|
|
|
OR ({t}.reviewtype = 'pending' AND {t}.action = %s))"""
|
|
|
|
|
|
|
|
ids = '(%s)' % ', '.join(str(a.id) for a in addons)
|
|
|
|
versions_sql = sql.format(t=Version._meta.db_table, ids=ids)
|
|
|
|
approvals_sql = approvals_sql.format(t=Approval._meta.db_table, ids=ids)
|
|
|
|
|
|
|
|
versions = dict((x.addon_id, x) for x in
|
|
|
|
Version.objects.raw(versions_sql))
|
|
|
|
approvals = dict((x.addon_id, x) for x in
|
|
|
|
Approval.objects.raw(approvals_sql,
|
|
|
|
[amo.STATUS_NOMINATED,
|
|
|
|
amo.STATUS_PENDING]))
|
|
|
|
|
2009-12-31 01:43:25 +03:00
|
|
|
for addon in addons:
|
2010-01-29 02:47:01 +03:00
|
|
|
addon.version = versions.get(addon.id)
|
|
|
|
addon.approval = approvals.get(addon.id)
|
2009-12-31 01:43:25 +03:00
|
|
|
|
2010-04-14 02:59:28 +04:00
|
|
|
return jingo.render(request, 'zadmin/flagged_addon_list.html',
|
2009-12-31 01:43:25 +03:00
|
|
|
{'addons': addons})
|
2010-02-24 08:08:24 +03:00
|
|
|
|
|
|
|
|
2010-06-30 02:28:40 +04:00
|
|
|
@admin.site.admin_view
|
|
|
|
def hera(request):
|
|
|
|
form = FlushForm(initial={'flushprefix': site_settings.SITE_URL})
|
|
|
|
|
2010-08-10 21:40:50 +04:00
|
|
|
boxes = []
|
|
|
|
configured = False # Default to not showing the form.
|
|
|
|
for i in site_settings.HERA:
|
|
|
|
hera = get_hera(i)
|
|
|
|
r = {'location': urlparse(i['LOCATION'])[1], 'stats': False}
|
|
|
|
if hera:
|
|
|
|
r['stats'] = hera.getGlobalCacheInfo()
|
|
|
|
configured = True
|
|
|
|
boxes.append(r)
|
|
|
|
|
|
|
|
if not configured:
|
2010-07-23 01:18:37 +04:00
|
|
|
messages.error(request, "Hera is not (or mis-)configured.")
|
2010-06-30 02:28:40 +04:00
|
|
|
form = None
|
|
|
|
|
2010-07-23 01:18:37 +04:00
|
|
|
if request.method == 'POST' and hera:
|
2010-06-30 02:28:40 +04:00
|
|
|
form = FlushForm(request.POST)
|
|
|
|
if form.is_valid():
|
|
|
|
expressions = request.POST['flushlist'].splitlines()
|
|
|
|
|
2010-07-23 01:18:37 +04:00
|
|
|
for url in expressions:
|
|
|
|
num = flush_urls([url], request.POST['flushprefix'], True)
|
|
|
|
msg = ("Flushed %d objects from front end cache for: %s"
|
|
|
|
% (len(num), url))
|
2010-06-30 02:28:40 +04:00
|
|
|
log.info("[Hera] (user:%s) %s" % (request.user, msg))
|
|
|
|
messages.success(request, msg)
|
|
|
|
|
|
|
|
return jingo.render(request, 'zadmin/hera.html',
|
2010-08-10 21:40:50 +04:00
|
|
|
{'form': form, 'boxes': boxes})
|
2010-06-30 02:28:40 +04:00
|
|
|
|
2010-02-24 08:08:24 +03:00
|
|
|
|
|
|
|
@admin.site.admin_view
|
|
|
|
def settings(request):
|
2010-08-11 00:56:34 +04:00
|
|
|
settings_dict = debug.get_safe_settings()
|
|
|
|
|
|
|
|
# sigh
|
|
|
|
settings_dict['HERA'] = []
|
|
|
|
for i in site_settings.HERA:
|
|
|
|
settings_dict['HERA'].append(debug.cleanse_setting('HERA', i))
|
|
|
|
|
2011-03-21 22:58:16 +03:00
|
|
|
for i in ['PAYPAL_EMBEDDED_AUTH', 'PAYPAL_CGI_AUTH']:
|
|
|
|
settings_dict[i] = debug.cleanse_setting(i, getattr(site_settings, i))
|
|
|
|
|
2010-04-14 02:59:28 +04:00
|
|
|
return jingo.render(request, 'zadmin/settings.html',
|
2010-08-11 00:56:34 +04:00
|
|
|
{'settings_dict': settings_dict})
|
2010-03-25 01:52:02 +03:00
|
|
|
|
|
|
|
|
|
|
|
@admin.site.admin_view
|
|
|
|
def env(request):
|
|
|
|
return http.HttpResponse(u'<pre>%s</pre>' % (jinja2.escape(request)))
|
2011-03-03 05:25:38 +03:00
|
|
|
|
|
|
|
|
|
|
|
@admin.site.admin_view
|
|
|
|
def fix_disabled_file(request):
|
|
|
|
file_ = None
|
|
|
|
if request.method == 'POST' and 'file' in request.POST:
|
|
|
|
file_ = get_object_or_404(File, id=request.POST['file'])
|
|
|
|
if 'confirm' in request.POST:
|
|
|
|
file_.unhide_disabled_file()
|
|
|
|
messages.success(request, 'We have done a great thing.')
|
|
|
|
return redirect('zadmin.fix-disabled')
|
|
|
|
return jingo.render(request, 'zadmin/fix-disabled.html',
|
|
|
|
{'file': file_,
|
|
|
|
'file_id': request.POST.get('file', '')})
|
2011-04-27 00:07:59 +04:00
|
|
|
|
|
|
|
|
|
|
|
@login_required
|
|
|
|
@post_required
|
|
|
|
@json_view
|
|
|
|
def application_versions_json(request):
|
|
|
|
app_id = request.POST['application_id']
|
|
|
|
f = BulkValidationForm()
|
|
|
|
return {'choices': f.version_choices_for_app_id(app_id)}
|
|
|
|
|
|
|
|
|
|
|
|
@admin.site.admin_view
|
|
|
|
def validation(request, form=None):
|
|
|
|
if not form:
|
|
|
|
form = BulkValidationForm()
|
2011-04-29 02:27:57 +04:00
|
|
|
jobs = ValidationJob.objects.order_by('-created')
|
2011-04-27 00:07:59 +04:00
|
|
|
return jingo.render(request, 'zadmin/validation.html',
|
2011-05-04 22:56:44 +04:00
|
|
|
{'form': form,
|
2011-05-09 23:40:16 +04:00
|
|
|
'success_form': NotifyForm(text='success'),
|
|
|
|
'failure_form': NotifyForm(text='failure'),
|
2011-05-04 22:56:44 +04:00
|
|
|
'validation_jobs': jobs})
|
2011-04-27 00:07:59 +04:00
|
|
|
|
|
|
|
|
2011-05-20 00:51:33 +04:00
|
|
|
def find_files(job):
|
2011-05-21 00:25:52 +04:00
|
|
|
# This is a first pass, we know we don't want any addons in the states
|
|
|
|
# STATUS_NULL and STATUS_DISABLED.
|
|
|
|
addons = (Addon.objects.filter(status__in=amo.VALID_STATUSES,
|
2011-05-20 00:51:33 +04:00
|
|
|
disabled_by_user=False,
|
|
|
|
versions__apps__application=job.application.id,
|
|
|
|
versions__apps__max=job.curr_max_version.id)
|
2011-05-21 00:25:52 +04:00
|
|
|
.no_transforms().values_list("pk", flat=True)
|
|
|
|
.distinct())
|
2011-05-20 00:51:33 +04:00
|
|
|
for pks in chunked(addons, 100):
|
|
|
|
tasks.add_validation_jobs.delay(pks, job.pk)
|
2011-05-19 23:13:11 +04:00
|
|
|
|
|
|
|
|
2011-04-27 00:07:59 +04:00
|
|
|
@admin.site.admin_view
|
|
|
|
def start_validation(request):
|
|
|
|
form = BulkValidationForm(request.POST)
|
|
|
|
if form.is_valid():
|
2011-05-20 00:51:33 +04:00
|
|
|
job = form.save(commit=False)
|
|
|
|
job.creator = get_user()
|
|
|
|
job.save()
|
|
|
|
find_files(job)
|
2011-04-27 00:07:59 +04:00
|
|
|
return redirect(reverse('zadmin.validation'))
|
|
|
|
else:
|
|
|
|
return validation(request, form=form)
|
2011-05-03 23:31:33 +04:00
|
|
|
|
|
|
|
|
|
|
|
def completed_versions_dirty(job):
|
|
|
|
"""Given a job, calculate which unique versions could need updating."""
|
|
|
|
return (Version.objects
|
2011-05-05 21:31:18 +04:00
|
|
|
.filter(files__validation_results__validation_job=job,
|
|
|
|
files__validation_results__errors=0,
|
|
|
|
files__validation_results__completed__isnull=False)
|
|
|
|
.values_list('pk', flat=True).distinct())
|
2011-05-03 23:31:33 +04:00
|
|
|
|
2011-05-04 22:56:44 +04:00
|
|
|
|
|
|
|
@post_required
|
|
|
|
@admin.site.admin_view
|
|
|
|
@json_view
|
|
|
|
def notify_syntax(request):
|
|
|
|
notify_form = NotifyForm(request.POST)
|
|
|
|
if not notify_form.is_valid():
|
|
|
|
return {'valid': False, 'error': notify_form.errors['text'][0]}
|
|
|
|
else:
|
|
|
|
return {'valid': True, 'error': None}
|
|
|
|
|
|
|
|
|
|
|
|
@post_required
|
|
|
|
@admin.site.admin_view
|
2011-05-09 23:40:16 +04:00
|
|
|
def notify_failure(request, job):
|
2011-05-04 22:56:44 +04:00
|
|
|
job = get_object_or_404(ValidationJob, pk=job)
|
2011-05-09 23:40:16 +04:00
|
|
|
notify_form = NotifyForm(request.POST, text='failure')
|
2011-05-04 22:56:44 +04:00
|
|
|
|
|
|
|
if not notify_form.is_valid():
|
2011-05-09 23:40:16 +04:00
|
|
|
messages.error(request, notify_form)
|
2011-05-04 22:56:44 +04:00
|
|
|
|
|
|
|
else:
|
|
|
|
file_pks = job.result_failing().values_list('file_id', flat=True)
|
|
|
|
for chunk in chunked(file_pks, 100):
|
|
|
|
tasks.notify_failed.delay(chunk, job.pk, notify_form.cleaned_data)
|
|
|
|
messages.success(request, _('Notifying authors task started.'))
|
|
|
|
|
|
|
|
return redirect(reverse('zadmin.validation'))
|
2011-05-09 23:40:16 +04:00
|
|
|
|
|
|
|
|
|
|
|
@post_required
|
|
|
|
@admin.site.admin_view
|
|
|
|
def notify_success(request, job):
|
|
|
|
job = get_object_or_404(ValidationJob, pk=job)
|
|
|
|
notify_form = NotifyForm(request.POST, text='success')
|
|
|
|
|
|
|
|
if not notify_form.is_valid():
|
|
|
|
messages.error(request, notify_form.errors)
|
|
|
|
|
|
|
|
else:
|
|
|
|
versions = completed_versions_dirty(job)
|
|
|
|
for chunk in chunked(versions, 100):
|
|
|
|
tasks.notify_success.delay(chunk, job.pk, notify_form.cleaned_data)
|
|
|
|
messages.success(request, _('Updating max version task and '
|
|
|
|
'notifying authors started.'))
|
|
|
|
|
|
|
|
return redirect(reverse('zadmin.validation'))
|
2011-05-17 02:20:53 +04:00
|
|
|
|
|
|
|
|
|
|
|
@admin.site.admin_view
|
|
|
|
def email_preview_csv(request, topic):
|
|
|
|
resp = http.HttpResponse()
|
|
|
|
resp['Content-Type'] = 'text/csv; charset=utf-8'
|
|
|
|
resp['Content-Disposition'] = "attachment; filename=%s.csv" % (topic)
|
|
|
|
writer = csv.writer(resp)
|
|
|
|
fields = ['from_email', 'recipient_list', 'subject', 'body']
|
|
|
|
writer.writerow(fields)
|
|
|
|
rs = EmailPreviewTopic(topic=topic).filter().values_list(*fields)
|
|
|
|
for row in rs:
|
|
|
|
writer.writerow([r.encode('utf8') for r in row])
|
|
|
|
return resp
|
2011-05-20 02:53:06 +04:00
|
|
|
|
|
|
|
|
|
|
|
@admin.site.admin_view
|
|
|
|
def jetpack(request):
|
|
|
|
cfg = Config.objects.get(key='jetpack_version')
|
|
|
|
if request.method == 'POST':
|
|
|
|
cfg.value = request.POST['jetpack_version']
|
|
|
|
cfg.save()
|
|
|
|
return redirect('zadmin.jetpack')
|
|
|
|
q = File.objects.filter(jetpack_version__isnull=False)
|
|
|
|
jetpacks = q.aggregate(num=Count('id'))['num']
|
|
|
|
by_version = q.values_list('jetpack_version').annotate(Count('id'))
|
|
|
|
return jingo.render(request, 'zadmin/jetpack.html',
|
|
|
|
dict(cfg=cfg, jetpacks=jetpacks,
|
|
|
|
by_version=by_version))
|