Badge nominations and misc tweaks

* Nomination submission view, to create new nominations
* Nomination detail view, affords approval and acceptance
* Award model proxy that produces multiplayer Badge proxies
This commit is contained in:
Les Orchard 2011-09-30 01:48:10 -04:00 коммит произвёл Les Orchard
Родитель f66d12bac2
Коммит 84abb3a449
8 изменённых файлов: 207 добавлений и 50 удалений

Просмотреть файл

@ -12,33 +12,10 @@ except ImportError, e:
from django.utils.translation import ugettext_lazy as _
from badger.models import (Award)
from badger.forms import (MyModelForm, MyForm)
from badger_multiplayer.models import (Badge, Nomination)
class MyModelForm(forms.ModelForm):
def as_ul(self):
"Returns this form rendered as HTML <li>s -- excluding the <ul></ul>."
return self._html_output(
normal_row=(u'<li%(html_class_attr)s>%(label)s %(field)s' +
'%(help_text)s%(errors)s</li>'),
error_row=u'<li>%s</li>',
row_ender='</li>',
help_text_html=u' <p class="help">%s</p>',
errors_on_separate_row=False)
class MyForm(forms.Form):
def as_ul(self):
"Returns this form rendered as HTML <li>s -- excluding the <ul></ul>."
return self._html_output(
normal_row=(u'<li%(html_class_attr)s>%(label)s %(field)s' +
'%(help_text)s%(errors)s</li>'),
error_row=u'<li>%s</li>',
row_ender='</li>',
help_text_html=u' <p class="help">%s</p>',
errors_on_separate_row=False)
class BadgeEditForm(MyModelForm):
class Meta:
@ -63,3 +40,10 @@ class BadgeNewForm(BadgeEditForm):
super(BadgeNewForm, self).__init__(*args, **kwargs)
#if not settings.RECAPTCHA_PRIVATE_KEY:
# del self.fields['captcha']
class BadgeSubmitNominationForm(MyModelForm):
class Meta:
model = Nomination
fields = ('nominee', )

Просмотреть файл

@ -18,14 +18,33 @@ from badger.models import (Award, BadgerException,
from .signals import (nomination_will_be_approved, nomination_was_approved,
nomination_will_be_accepted, nomination_was_accepted,
user_will_be_nominated, user_was_nominated,)
user_will_be_nominated, user_was_nominated, )
class Badge(badger.models.Badge):
"""Enhanced Badge model with multiplayer features"""
class Meta:
proxy = True
get_permissions_for = get_permissions_for
def allows_nominate_for(self, user):
"""Is nominate_for() allowed for this user?"""
if None == user:
return True
if user.is_anonymous():
return False
if user.is_staff or user.is_superuser:
return True
if user == self.creator:
return True
# TODO: Flag to enable / disable nominations from anyone
# TODO: List of delegates from whom nominations are accepted
return True
def nominate_for(self, nominee, nominator=None):
"""Nominate a nominee for this badge on the nominator's behalf"""
return Nomination.objects.create(badge=self, creator=nominator,
@ -35,6 +54,20 @@ class Badge(badger.models.Badge):
return Nomination.objects.filter(nominee=user, badge=self).count() > 0
class Award(badger.models.Award):
"""Enhanced Award model with multiplayer features"""
class Meta:
proxy = True
@property
def badge(self):
"""Property that wraps the related badge in a multiplayer upgrade"""
new_inst = Badge()
new_inst.__dict__ = super(Award, self).badge.__dict__
return new_inst
class NominationException(BadgerException):
"""Nomination model exception"""
@ -88,7 +121,14 @@ class Nomination(models.Model):
nomination=self)
if self.is_approved() and self.is_accepted():
self.award = self.badge.award_to(self.nominee, self.approver)
# HACK: Convert the original-flavor Award into a multiplayer Award
# before assigning to self.
real_award = self.badge.award_to(self.nominee, self.approver)
award = Award()
award.__dict__ = real_award.__dict__
self.award = award
# This was the original code, which caused errors:
# self.award = self.badge.award_to(self.nominee, self.approver)
super(Nomination, self).save(*args, **kwargs)
@ -96,11 +136,26 @@ class Nomination(models.Model):
user_was_nominated.send(sender=self.__class__,
nomination=self)
def allows_detail_by(self, user):
if (user.is_staff or
user.is_superuser or
user == self.badge.creator or
user == self.nominee or
user == self.creator ):
return True
# TODO: List of delegates empowered by badge creator to approve nominations
return False
def allows_approve_by(self, user):
if user.is_staff or user.is_superuser:
return True
if user == self.badge.creator:
return True
# TODO: List of delegates empowered by badge creator to approve nominations
return False
def approve_by(self, approver):

Просмотреть файл

@ -1,21 +0,0 @@
<dl class="badge" data-slug="{{ badge.slug }}">
{% if award and award.image %}
<dt>Image:</dt>
<dd class="image"><img src="{{ award.image.url }}" width="256" height="256" /></dd>
{% elif badge.image %}
<dt>Image:</dt>
<dd class="image"><img src="{{ badge.image.url }}" width="256" height="256" /></dd>
{% endif %}
<dt>Title:</dt>
<dd class="title">{{ badge.title }}</dd>
{% if badge.description %}
<dt>Description:<dt>
<dd class="description">{{ badge.description }}</dd>
{% endif %}
{% if badge.allows_edit_by(request.user) %}
<dt>Actions:</dt>
<dd><ul>
<li><a class="edit_badge" href="{{ url('badger_multiplayer.views.edit', badge.slug) }}">edit</a></li>
</ul></dd>
{% endif %}
</dl>

Просмотреть файл

@ -0,0 +1,22 @@
{% extends "badger/base.html" %}
{% block pageid %}badge_nominate{% endblock %}
{% block content %}
<h2>Submit a badge nomination</h2>
<form id="nominate_badge" method="POST" action="" enctype="multipart/form-data">
{{ csrf() }}
<ul>
{{ form.as_ul() }}
<li><input type="submit" class="submit" value="Nominate"></li>
</ul>
</form>
<section class="badge">
<h3>Badge</h3>
{% include "badger/includes/badge_full.html" %}
</section>
{% endblock %}

Просмотреть файл

@ -0,0 +1,6 @@
{% if badge.allows_nominate_for(request.user) %}
<li><a class="edit_badge" href="{{ url('badger_multiplayer.views.nominate_for', badge.slug) }}">{{ _('Submit nomination') }}</a></li>
{% endif %}
{% if badge.allows_edit_by(request.user) %}
<li><a class="edit_badge" href="{{ url('badger_multiplayer.views.edit', badge.slug) }}">{{ _('Edit badge') }}</a></li>
{% endif %}

Просмотреть файл

@ -0,0 +1,58 @@
{% extends "badger/base.html" %}
{% block pageid %}nomination_detail{% endblock %}
{% block content %}
<section class="nomination">
<h2>Nomination</h2>
<dl>
{% set show_approve = not nomination.is_approved() and nomination.allows_approve_by(request.user) %}
{% set show_accept = not nomination.is_accepted() and nomination.allows_accept(request.user) %}
{% if show_approve or show_accept %}
<dt>Actions:</dt><dd><ul>
{% if show_approve %}
<li><form method="POST">
{{ csrf() }}
<input type="hidden" name="action" value="approve_by" />
<button>{{ _('Approve nomination') }}</button>
</form></li>
{% endif %}
{% if show_accept %}
<li><form method="POST">
{{ csrf() }}
<input type="hidden" name="action" value="accept" />
<button>{{ _('Accept nomination') }}</button>
</form></li>
{% endif %}
</ul></dd>
{% endif %}
{% if nomination.award %}
<dt>Award:</dt>
<dd><a href="{{ nomination.award.get_absolute_url() }}">{{ nomination.award }}</a></dd>
{% endif %}
<dt>Accepted?:</dt>
<dd>{{ nomination.accepted and "Yes" or "No" }}</dd>
<dt>Approved?:</dt>
<dd>{{ nomination.approver and "Yes" or "No" }}</dd>
<dt>Nominee:</dt>
<dd><a href="{{ nomination.nominee.get_absolute_url() }}">{{ nomination.nominee }}</a></dd>
<dt>Nominator:</dt>
<dd><a href="{{ nomination.creator.get_absolute_url() }}">{{ nomination.creator }}</a></dd>
{% if nomination.approver %}
<dt>Approver:</dt>
<dd><a href="{{ nomination.approver.get_absolute_url() }}">{{ nomination.approver }}</a></dd>
{% endif %}
<dt>Created:</dt>
<dd>{{ nomination.created }}</dd>
<dt>Modified:</dt>
<dd>{{ nomination.modified }}</dd>
</dl>
</section>
<section class="badge">
<h3>Badge</h3>
{% include "badger/includes/badge_full.html" %}
</section>
{% endblock %}

Просмотреть файл

@ -8,10 +8,14 @@ from .feeds import BadgesRecentFeed
urlpatterns = patterns('badger_multiplayer.views',
url(r'^create$', 'create',
url(r'^;create$', 'create',
name='badger_multiplayer.create_badge'),
url(r'^badges/(?P<slug>[^/]+)/edit$', 'edit',
url(r'^detail/(?P<slug>[^/]+);nominate$', 'nominate_for',
name='badger_multiplayer.nominate_for'),
url(r'^detail/(?P<slug>[^/]+);edit$', 'edit',
name='badger_multiplayer.badge_edit'),
url(r'^detail/(?P<slug>[^/]+)/nominations/(?P<id>[^/]+)/?$', 'nomination_detail',
name='badger.nomination_detail'),
url(r'^feeds/(?P<format>[^/]+)/badges/?$', BadgesRecentFeed(),
name="badger_multiplayer.feeds.badges_recent"),

Просмотреть файл

@ -36,7 +36,8 @@ from badger_multiplayer.models import (Badge, Nomination,
NominationApproveNotAllowedException,
NominationAcceptNotAllowedException)
from badger_multiplayer.forms import (BadgeNewForm, BadgeEditForm)
from badger_multiplayer.forms import (BadgeNewForm, BadgeEditForm,
BadgeSubmitNominationForm)
@require_http_methods(['GET', 'POST'])
@ -83,3 +84,51 @@ def edit(request, slug):
return render_to_response('badger_multiplayer/badge_edit.html', dict(
badge=badge, form=form,
), context_instance=RequestContext(request))
@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)
return HttpResponseRedirect(reverse(
'badger_multiplayer.views.nomination_detail',
args=(slug, id)))
return render_to_response('badger_multiplayer/nomination_detail.html', dict(
badge=badge, nomination=nomination,
), context_instance=RequestContext(request))
@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():
award = badge.nominate_for(form.cleaned_data['nominee'],
request.user)
return HttpResponseRedirect(reverse(
'badger_multiplayer.views.nomination_detail',
args=(badge.slug, award.id, )))
return render_to_response('badger_multiplayer/badge_nominate_for.html', dict(
form=form, badge=badge,
), context_instance=RequestContext(request))