546 строки
20 KiB
Python
546 строки
20 KiB
Python
import json
|
|
import logging
|
|
import random
|
|
|
|
from django.conf import settings
|
|
from django.http import (HttpResponseRedirect, HttpResponse,
|
|
HttpResponseForbidden, HttpResponseNotFound, Http404)
|
|
from django.shortcuts import get_object_or_404, render
|
|
from django.template import RequestContext
|
|
from django.template.defaultfilters import slugify
|
|
|
|
try:
|
|
from funfactory.urlresolvers import (get_url_prefix, Prefixer, reverse,
|
|
set_url_prefix)
|
|
from tower import activate
|
|
except ImportError:
|
|
from django.core.urlresolvers import reverse
|
|
|
|
try:
|
|
from tower import ugettext_lazy as _
|
|
except ImportError:
|
|
from django.utils.translation import ugettext_lazy as _
|
|
|
|
from django.views.generic.list import ListView
|
|
from django.views.decorators.http import (require_GET, require_POST,
|
|
require_http_methods)
|
|
|
|
from django.contrib import messages
|
|
|
|
from django.contrib.auth.decorators import login_required
|
|
from django.contrib.auth.models import User
|
|
|
|
try:
|
|
import taggit
|
|
from taggit.models import Tag, TaggedItem
|
|
except ImportError:
|
|
taggit = None
|
|
|
|
import badger
|
|
from badger import settings as bsettings
|
|
from .models import (Badge, Award, Nomination, DeferredAward,
|
|
Progress, BadgeAwardNotAllowedException,
|
|
BadgeAlreadyAwardedException,
|
|
NominationApproveNotAllowedException,
|
|
NominationAcceptNotAllowedException)
|
|
from .forms import (BadgeAwardForm, DeferredAwardGrantForm,
|
|
DeferredAwardMultipleGrantForm, BadgeNewForm,
|
|
BadgeEditForm, BadgeSubmitNominationForm)
|
|
|
|
|
|
def home(request):
|
|
"""Badger home page"""
|
|
badge_list = Badge.objects.order_by('-modified').all()[:bsettings.MAX_RECENT]
|
|
award_list = Award.objects.order_by('-modified').all()[:bsettings.MAX_RECENT]
|
|
badge_tags = Badge.objects.top_tags()
|
|
|
|
return render(request, '%s/home.html' % bsettings.TEMPLATE_BASE, dict(
|
|
badge_list=badge_list, award_list=award_list, badge_tags=badge_tags
|
|
))
|
|
|
|
|
|
class BadgesListView(ListView):
|
|
"""Badges list page"""
|
|
model = Badge
|
|
template_name = '%s/badges_list.html' % bsettings.TEMPLATE_BASE
|
|
template_object_name = 'badge'
|
|
paginate_by = bsettings.BADGE_PAGE_SIZE
|
|
|
|
def get_queryset(self):
|
|
qs = Badge.objects.order_by('-modified')
|
|
query_string = self.request.GET.get('q', None)
|
|
tag_name = self.kwargs.get('tag_name', None)
|
|
if query_string is not None:
|
|
sort_order = self.request.GET.get('sort', 'created')
|
|
qs = Badge.objects.search(query_string, sort_order)
|
|
if taggit and tag_name:
|
|
tag = get_object_or_404(Tag, name=tag_name)
|
|
qs = (Badge.objects.filter(tags__in=[tag]).distinct())
|
|
return qs
|
|
|
|
def get_context_data(self, **kwargs):
|
|
context = super(BadgesListView, self).get_context_data(**kwargs)
|
|
context['award_list'] = None
|
|
context['tag_name'] = self.kwargs.get('tag_name', None)
|
|
context['query_string'] = kwargs.get('q', None)
|
|
if context['query_string'] is not None:
|
|
# TODO: Is this the most efficient query?
|
|
context['award_list'] = (Award.objects.filter(badge__in=self.get_queryset()))
|
|
if taggit and context['tag_name']:
|
|
# TODO: Is this the most efficient query?
|
|
context['award_list'] = (Award.objects.filter(badge__in=self.get_queryset()))
|
|
return context
|
|
|
|
badges_list = BadgesListView.as_view()
|
|
|
|
|
|
@require_http_methods(['HEAD', 'GET', 'POST'])
|
|
def detail(request, slug, format="html"):
|
|
"""Badge detail view"""
|
|
badge = get_object_or_404(Badge, slug=slug)
|
|
if not badge.allows_detail_by(request.user):
|
|
return HttpResponseForbidden('Detail forbidden')
|
|
|
|
awards = (Award.objects.filter(badge=badge)
|
|
.order_by('-created'))[:bsettings.MAX_RECENT]
|
|
|
|
# FIXME: This is awkward. It used to collect sections as responses to a
|
|
# signal sent out to badger_multiplayer and hypothetical future expansions
|
|
# to badger
|
|
sections = dict()
|
|
sections['award'] = dict(form=BadgeAwardForm())
|
|
if badge.allows_nominate_for(request.user):
|
|
sections['nominate'] = dict(form=BadgeSubmitNominationForm())
|
|
|
|
if request.method == "POST":
|
|
|
|
if request.POST.get('is_generate', None):
|
|
if not badge.allows_manage_deferred_awards_by(request.user):
|
|
return HttpResponseForbidden('Claim generate denied')
|
|
amount = int(request.POST.get('amount', 10))
|
|
reusable = (amount == 1)
|
|
cg = badge.generate_deferred_awards(user=request.user,
|
|
amount=amount,
|
|
reusable=reusable)
|
|
|
|
if request.POST.get('is_delete', None):
|
|
if not badge.allows_manage_deferred_awards_by(request.user):
|
|
return HttpResponseForbidden('Claim delete denied')
|
|
group = request.POST.get('claim_group')
|
|
badge.delete_claim_group(request.user, group)
|
|
|
|
url = reverse('badger.views.detail', kwargs=dict(slug=slug))
|
|
return HttpResponseRedirect(url)
|
|
|
|
claim_groups = badge.claim_groups
|
|
|
|
if format == 'json':
|
|
data = badge.as_obi_serialization(request)
|
|
resp = HttpResponse(json.dumps(data))
|
|
resp['Content-Type'] = 'application/json'
|
|
return resp
|
|
else:
|
|
return render(request, '%s/badge_detail.html' % bsettings.TEMPLATE_BASE, dict(
|
|
badge=badge, award_list=awards, sections=sections,
|
|
claim_groups=claim_groups
|
|
))
|
|
|
|
|
|
@require_http_methods(['GET', 'POST'])
|
|
@login_required
|
|
def create(request):
|
|
"""Create a new badge"""
|
|
if not Badge.objects.allows_add_by(request.user):
|
|
return HttpResponseForbidden()
|
|
|
|
if request.method != "POST":
|
|
form = BadgeNewForm()
|
|
form.initial['tags'] = request.GET.get('tags', '')
|
|
else:
|
|
form = BadgeNewForm(request.POST, request.FILES)
|
|
if form.is_valid():
|
|
new_sub = form.save(commit=False)
|
|
new_sub.creator = request.user
|
|
new_sub.save()
|
|
form.save_m2m()
|
|
return HttpResponseRedirect(reverse(
|
|
'badger.views.detail', args=(new_sub.slug,)))
|
|
|
|
return render(request, '%s/badge_create.html' % bsettings.TEMPLATE_BASE, dict(
|
|
form=form,
|
|
))
|
|
|
|
|
|
@require_http_methods(['GET', 'POST'])
|
|
@login_required
|
|
def edit(request, slug):
|
|
"""Edit an existing badge"""
|
|
badge = get_object_or_404(Badge, slug=slug)
|
|
if not badge.allows_edit_by(request.user):
|
|
return HttpResponseForbidden()
|
|
|
|
if request.method != "POST":
|
|
form = BadgeEditForm(instance=badge)
|
|
else:
|
|
form = BadgeEditForm(request.POST, request.FILES, instance=badge)
|
|
if form.is_valid():
|
|
new_sub = form.save(commit=False)
|
|
new_sub.save()
|
|
form.save_m2m()
|
|
return HttpResponseRedirect(reverse(
|
|
'badger.views.detail', args=(new_sub.slug,)))
|
|
|
|
return render(request, '%s/badge_edit.html' % bsettings.TEMPLATE_BASE, dict(
|
|
badge=badge, form=form,
|
|
))
|
|
|
|
|
|
@require_http_methods(['GET', 'POST'])
|
|
@login_required
|
|
def delete(request, slug):
|
|
"""Delete a badge"""
|
|
badge = get_object_or_404(Badge, slug=slug)
|
|
if not badge.allows_delete_by(request.user):
|
|
return HttpResponseForbidden()
|
|
|
|
awards_count = badge.award_set.count()
|
|
|
|
if request.method == "POST":
|
|
messages.info(request, _(u'Badge "{badgetitle}" deleted.').format(
|
|
badgetitle=badge.title))
|
|
badge.delete()
|
|
return HttpResponseRedirect(reverse('badger.views.badges_list'))
|
|
|
|
return render(request, '%s/badge_delete.html' % bsettings.TEMPLATE_BASE, dict(
|
|
badge=badge, awards_count=awards_count,
|
|
))
|
|
|
|
|
|
@require_http_methods(['GET', 'POST'])
|
|
@login_required
|
|
def award_badge(request, slug):
|
|
"""Issue an award for a badge"""
|
|
badge = get_object_or_404(Badge, slug=slug)
|
|
if not badge.allows_award_to(request.user):
|
|
return HttpResponseForbidden('Award forbidden')
|
|
|
|
if request.method != "POST":
|
|
form = BadgeAwardForm()
|
|
else:
|
|
form = BadgeAwardForm(request.POST, request.FILES)
|
|
if form.is_valid():
|
|
emails = form.cleaned_data['emails']
|
|
description = form.cleaned_data['description']
|
|
for email in emails:
|
|
result = badge.award_to(email=email, awarder=request.user,
|
|
description=description)
|
|
if result:
|
|
if not hasattr(result, 'claim_code'):
|
|
messages.info(request, _(u'Award issued to {email}').format(
|
|
email=email))
|
|
else:
|
|
messages.info(request, _(
|
|
u'Invitation to claim award sent to {email}').format(email=email))
|
|
return HttpResponseRedirect(reverse('badger.views.detail',
|
|
args=(badge.slug,)))
|
|
|
|
return render(request, '%s/badge_award.html' % bsettings.TEMPLATE_BASE, dict(
|
|
form=form, badge=badge,
|
|
))
|
|
|
|
|
|
class AwardsListView(ListView):
|
|
model = Award
|
|
template_name = '%s/awards_list.html' % bsettings.TEMPLATE_BASE
|
|
template_object_name = 'award'
|
|
paginate_by = bsettings.BADGE_PAGE_SIZE
|
|
|
|
def get_badge(self):
|
|
if not hasattr(self, 'badge'):
|
|
self._badge = get_object_or_404(Badge, slug=self.kwargs.get('slug', None))
|
|
return self._badge
|
|
|
|
def get_queryset(self):
|
|
qs = Award.objects.order_by('-modified')
|
|
if self.kwargs.get('slug', None) is not None:
|
|
qs = qs.filter(badge=self.get_badge())
|
|
return qs
|
|
|
|
def get_context_data(self, **kwargs):
|
|
context = super(AwardsListView, self).get_context_data(**kwargs)
|
|
if self.kwargs.get('slug', None) is None:
|
|
context['badge'] = None
|
|
else:
|
|
context['badge'] = self.get_badge()
|
|
return context
|
|
|
|
awards_list = AwardsListView.as_view()
|
|
|
|
|
|
@require_http_methods(['HEAD', 'GET'])
|
|
def award_detail(request, slug, id, format="html"):
|
|
"""Award detail view"""
|
|
badge = get_object_or_404(Badge, slug=slug)
|
|
award = get_object_or_404(Award, badge=badge, pk=id)
|
|
if not award.allows_detail_by(request.user):
|
|
return HttpResponseForbidden('Award detail forbidden')
|
|
|
|
if format == 'json':
|
|
data = json.dumps(award.as_obi_assertion(request))
|
|
resp = HttpResponse(data)
|
|
resp['Content-Type'] = 'application/json'
|
|
return resp
|
|
else:
|
|
return render(request, '%s/award_detail.html' % bsettings.TEMPLATE_BASE, dict(
|
|
badge=badge, award=award,
|
|
))
|
|
|
|
|
|
@require_http_methods(['GET', 'POST'])
|
|
@login_required
|
|
def award_delete(request, slug, id):
|
|
"""Delete an award"""
|
|
badge = get_object_or_404(Badge, slug=slug)
|
|
award = get_object_or_404(Award, badge=badge, pk=id)
|
|
if not award.allows_delete_by(request.user):
|
|
return HttpResponseForbidden('Award delete forbidden')
|
|
|
|
if request.method == "POST":
|
|
messages.info(request, _(u'Award for badge "{badgetitle}" deleted.').format(
|
|
badgetitle=badge.title))
|
|
award.delete()
|
|
url = reverse('badger.views.detail', kwargs=dict(slug=slug))
|
|
return HttpResponseRedirect(url)
|
|
|
|
return render(request, '%s/award_delete.html' % bsettings.TEMPLATE_BASE, dict(
|
|
badge=badge, award=award
|
|
))
|
|
|
|
|
|
@login_required
|
|
def _do_claim(request, deferred_award):
|
|
"""Perform claim of a deferred award"""
|
|
if not deferred_award.allows_claim_by(request.user):
|
|
return HttpResponseForbidden('Claim denied')
|
|
award = deferred_award.claim(request.user)
|
|
if award:
|
|
url = reverse('badger.views.award_detail',
|
|
args=(award.badge.slug, award.id,))
|
|
return HttpResponseRedirect(url)
|
|
|
|
|
|
def _redirect_to_claimed_awards(awards, awards_ct):
|
|
# Has this claim code already been used for awards?
|
|
# If so, then a GET redirects to an award detail or list
|
|
if awards_ct == 1:
|
|
award = awards[0]
|
|
url = reverse('badger.views.award_detail',
|
|
args=(award.badge.slug, award.id,))
|
|
return HttpResponseRedirect(url)
|
|
elif awards_ct > 1:
|
|
award = awards[0]
|
|
url = reverse('badger.views.awards_list',
|
|
args=(award.badge.slug,))
|
|
return HttpResponseRedirect(url)
|
|
|
|
|
|
@require_http_methods(['GET', 'POST'])
|
|
def claim_deferred_award(request, claim_code=None):
|
|
"""Deferred award detail view"""
|
|
if not claim_code:
|
|
claim_code = request.REQUEST.get('code', '').strip()
|
|
|
|
# Look for any awards that match this claim code.
|
|
awards = Award.objects.filter(claim_code=claim_code)
|
|
awards_ct = awards.count()
|
|
|
|
# Try fetching a DeferredAward matching the claim code. If none found, then
|
|
# make one last effort to redirect a POST to awards. Otherwise, 404
|
|
try:
|
|
deferred_award = DeferredAward.objects.get(claim_code=claim_code)
|
|
|
|
# If this is a GET and there are awards matching the claim code,
|
|
# redirect to the awards.
|
|
if (request.method == "GET" and awards_ct > 0 and
|
|
not deferred_award.reusable):
|
|
return _redirect_to_claimed_awards(awards, awards_ct)
|
|
|
|
except DeferredAward.DoesNotExist:
|
|
if awards_ct > 0:
|
|
return _redirect_to_claimed_awards(awards, awards_ct)
|
|
else:
|
|
raise Http404('No such claim code, %s' % claim_code)
|
|
|
|
if not deferred_award.allows_detail_by(request.user):
|
|
return HttpResponseForbidden('Claim detail denied')
|
|
|
|
if request.method != "POST":
|
|
grant_form = DeferredAwardGrantForm()
|
|
else:
|
|
grant_form = DeferredAwardGrantForm(request.POST, request.FILES)
|
|
if not request.POST.get('is_grant', False) is not False:
|
|
return _do_claim(request, deferred_award)
|
|
else:
|
|
if not deferred_award.allows_grant_by(request.user):
|
|
return HttpResponseForbidden('Grant denied')
|
|
if grant_form.is_valid():
|
|
email = request.POST.get('email', None)
|
|
deferred_award.grant_to(email=email, granter=request.user)
|
|
messages.info(request, _(u'Award claim granted to {email}').format(
|
|
email=email))
|
|
url = reverse('badger.views.detail',
|
|
args=(deferred_award.badge.slug,))
|
|
return HttpResponseRedirect(url)
|
|
|
|
return render(request, '%s/claim_deferred_award.html' % bsettings.TEMPLATE_BASE, dict(
|
|
badge=deferred_award.badge, deferred_award=deferred_award,
|
|
grant_form=grant_form
|
|
))
|
|
|
|
|
|
@require_http_methods(['GET', 'POST'])
|
|
@login_required
|
|
def claims_list(request, slug, claim_group, format="html"):
|
|
"""Lists claims"""
|
|
badge = get_object_or_404(Badge, slug=slug)
|
|
if not badge.allows_manage_deferred_awards_by(request.user):
|
|
return HttpResponseForbidden()
|
|
|
|
deferred_awards = badge.get_claim_group(claim_group)
|
|
|
|
if format == "pdf":
|
|
from badger.printing import render_claims_to_pdf
|
|
return render_claims_to_pdf(request, slug, claim_group,
|
|
deferred_awards)
|
|
|
|
return render(request, '%s/claims_list.html' % bsettings.TEMPLATE_BASE, dict(
|
|
badge=badge, claim_group=claim_group,
|
|
deferred_awards=deferred_awards
|
|
))
|
|
|
|
|
|
@require_GET
|
|
def awards_by_user(request, username):
|
|
"""Badge awards by user"""
|
|
user = get_object_or_404(User, username=username)
|
|
awards = Award.objects.filter(user=user)
|
|
return render(request, '%s/awards_by_user.html' % bsettings.TEMPLATE_BASE, dict(
|
|
user=user, award_list=awards,
|
|
))
|
|
|
|
|
|
@require_GET
|
|
def awards_by_badge(request, slug):
|
|
"""Badge awards by badge"""
|
|
badge = get_object_or_404(Badge, slug=slug)
|
|
awards = Award.objects.filter(badge=badge)
|
|
return render(request, '%s/awards_by_badge.html' % bsettings.TEMPLATE_BASE, dict(
|
|
badge=badge, awards=awards,
|
|
))
|
|
|
|
|
|
@require_http_methods(['GET', 'POST'])
|
|
@login_required
|
|
def staff_tools(request):
|
|
"""HACK: This page offers miscellaneous tools useful to event staff.
|
|
Will go away in the future, addressed by:
|
|
https://github.com/mozilla/django-badger/issues/35
|
|
"""
|
|
if not (request.user.is_staff or request.user.is_superuser):
|
|
return HttpResponseForbidden()
|
|
|
|
if request.method != "POST":
|
|
grant_form = DeferredAwardMultipleGrantForm()
|
|
else:
|
|
if request.REQUEST.get('is_grant', False) is not False:
|
|
grant_form = DeferredAwardMultipleGrantForm(request.POST, request.FILES)
|
|
if grant_form.is_valid():
|
|
email = grant_form.cleaned_data['email']
|
|
codes = grant_form.cleaned_data['claim_codes']
|
|
for claim_code in codes:
|
|
da = DeferredAward.objects.get(claim_code=claim_code)
|
|
da.grant_to(email, request.user)
|
|
messages.info(request, _(u'Badge "{badgetitle}" granted to {email}').format(
|
|
badgetitle=da.badge, email=email))
|
|
url = reverse('badger.views.staff_tools')
|
|
return HttpResponseRedirect(url)
|
|
|
|
return render(request, '%s/staff_tools.html' % bsettings.TEMPLATE_BASE, dict(
|
|
grant_form=grant_form
|
|
))
|
|
|
|
|
|
@require_GET
|
|
def badges_by_user(request, username):
|
|
"""Badges created by user"""
|
|
user = get_object_or_404(User, username=username)
|
|
badges = Badge.objects.filter(creator=user)
|
|
return render(request, '%s/badges_by_user.html' % bsettings.TEMPLATE_BASE, dict(
|
|
user=user, badge_list=badges,
|
|
))
|
|
|
|
|
|
@require_http_methods(['GET', 'POST'])
|
|
@login_required
|
|
def nomination_detail(request, slug, id, format="html"):
|
|
"""Show details on a nomination, provide for approval and acceptance"""
|
|
badge = get_object_or_404(Badge, slug=slug)
|
|
nomination = get_object_or_404(Nomination, badge=badge, pk=id)
|
|
if not nomination.allows_detail_by(request.user):
|
|
return HttpResponseForbidden()
|
|
|
|
if request.method == "POST":
|
|
action = request.POST.get('action', '')
|
|
if action == 'approve_by':
|
|
nomination.approve_by(request.user)
|
|
elif action == 'accept':
|
|
nomination.accept(request.user)
|
|
elif action == 'reject_by':
|
|
nomination.reject_by(request.user)
|
|
return HttpResponseRedirect(reverse(
|
|
'badger.views.nomination_detail',
|
|
args=(slug, id)))
|
|
|
|
return render(request, '%s/nomination_detail.html' % bsettings.TEMPLATE_BASE,
|
|
dict(badge=badge, nomination=nomination,))
|
|
|
|
|
|
@require_http_methods(['GET', 'POST'])
|
|
@login_required
|
|
def nominate_for(request, slug):
|
|
"""Submit nomination for a badge"""
|
|
badge = get_object_or_404(Badge, slug=slug)
|
|
if not badge.allows_nominate_for(request.user):
|
|
return HttpResponseForbidden()
|
|
|
|
if request.method != "POST":
|
|
form = BadgeSubmitNominationForm()
|
|
else:
|
|
form = BadgeSubmitNominationForm(request.POST, request.FILES)
|
|
if form.is_valid():
|
|
emails = form.cleaned_data['emails']
|
|
for email in emails:
|
|
users = User.objects.filter(email=email)
|
|
if not users:
|
|
# TODO: Need a deferred nomination mechanism for
|
|
# non-registered users.
|
|
pass
|
|
else:
|
|
nominee = users[0]
|
|
try:
|
|
award = badge.nominate_for(nominee, request.user)
|
|
messages.info(request,
|
|
_(u'Nomination submitted for {email}').format(email=email))
|
|
except BadgeAlreadyAwardedException:
|
|
messages.info(request,
|
|
_(u'Badge already awarded to {email}').format(email=email))
|
|
except Exception:
|
|
messages.info(request,
|
|
_(u'Nomination failed for {email}').format(email=email))
|
|
|
|
return HttpResponseRedirect(reverse('badger.views.detail',
|
|
args=(badge.slug,)))
|
|
|
|
return render(request, '%s/badge_nominate_for.html' % bsettings.TEMPLATE_BASE,
|
|
dict(form=form, badge=badge,))
|