diff --git a/badger_multiplayer/forms.py b/badger_multiplayer/forms.py index 5162131..5ed5458 100644 --- a/badger_multiplayer/forms.py +++ b/badger_multiplayer/forms.py @@ -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
  • s -- excluding the ." - return self._html_output( - normal_row=(u'%(label)s %(field)s' + - '%(help_text)s%(errors)s
  • '), - error_row=u'
  • %s
  • ', - row_ender='', - help_text_html=u'

    %s

    ', - errors_on_separate_row=False) - - -class MyForm(forms.Form): - def as_ul(self): - "Returns this form rendered as HTML
  • s -- excluding the ." - return self._html_output( - normal_row=(u'%(label)s %(field)s' + - '%(help_text)s%(errors)s
  • '), - error_row=u'
  • %s
  • ', - row_ender='', - help_text_html=u'

    %s

    ', - 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', ) + diff --git a/badger_multiplayer/models.py b/badger_multiplayer/models.py index d527ef8..8f71d41 100644 --- a/badger_multiplayer/models.py +++ b/badger_multiplayer/models.py @@ -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): diff --git a/badger_multiplayer/templates/badger/includes/badge_full.html b/badger_multiplayer/templates/badger/includes/badge_full.html deleted file mode 100644 index b9f0191..0000000 --- a/badger_multiplayer/templates/badger/includes/badge_full.html +++ /dev/null @@ -1,21 +0,0 @@ -
    - {% if award and award.image %} -
    Image:
    -
    - {% elif badge.image %} -
    Image:
    -
    - {% endif %} -
    Title:
    -
    {{ badge.title }}
    - {% if badge.description %} -
    Description:
    -
    {{ badge.description }}
    - {% endif %} - {% if badge.allows_edit_by(request.user) %} -
    Actions:
    -
    - {% endif %} -
    diff --git a/badger_multiplayer/templates/badger_multiplayer/badge_nominate_for.html b/badger_multiplayer/templates/badger_multiplayer/badge_nominate_for.html new file mode 100644 index 0000000..998bb43 --- /dev/null +++ b/badger_multiplayer/templates/badger_multiplayer/badge_nominate_for.html @@ -0,0 +1,22 @@ +{% extends "badger/base.html" %} + +{% block pageid %}badge_nominate{% endblock %} + +{% block content %} + +

    Submit a badge nomination

    + +
    + {{ csrf() }} + +
    + +
    +

    Badge

    + {% include "badger/includes/badge_full.html" %} +
    + +{% endblock %} diff --git a/badger_multiplayer/templates/badger_multiplayer/includes/badge_full_actions.html b/badger_multiplayer/templates/badger_multiplayer/includes/badge_full_actions.html new file mode 100644 index 0000000..8a485ef --- /dev/null +++ b/badger_multiplayer/templates/badger_multiplayer/includes/badge_full_actions.html @@ -0,0 +1,6 @@ +{% if badge.allows_nominate_for(request.user) %} +
  • {{ _('Submit nomination') }}
  • +{% endif %} +{% if badge.allows_edit_by(request.user) %} +
  • {{ _('Edit badge') }}
  • +{% endif %} diff --git a/badger_multiplayer/templates/badger_multiplayer/nomination_detail.html b/badger_multiplayer/templates/badger_multiplayer/nomination_detail.html new file mode 100644 index 0000000..68270d8 --- /dev/null +++ b/badger_multiplayer/templates/badger_multiplayer/nomination_detail.html @@ -0,0 +1,58 @@ +{% extends "badger/base.html" %} + +{% block pageid %}nomination_detail{% endblock %} + +{% block content %} + +
    +

    Nomination

    +
    + {% 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 %} +
    Actions:
      + {% if show_approve %} +
    • + {{ csrf() }} + + +
    • + {% endif %} + {% if show_accept %} +
    • + {{ csrf() }} + + +
    • + {% endif %} +
    + {% endif %} + {% if nomination.award %} +
    Award:
    +
    {{ nomination.award }}
    + {% endif %} +
    Accepted?:
    +
    {{ nomination.accepted and "Yes" or "No" }}
    +
    Approved?:
    +
    {{ nomination.approver and "Yes" or "No" }}
    +
    Nominee:
    +
    {{ nomination.nominee }}
    +
    Nominator:
    +
    {{ nomination.creator }}
    + {% if nomination.approver %} +
    Approver:
    +
    {{ nomination.approver }}
    + {% endif %} +
    Created:
    +
    {{ nomination.created }}
    +
    Modified:
    +
    {{ nomination.modified }}
    +
    +
    + +
    +

    Badge

    + {% include "badger/includes/badge_full.html" %} +
    + +{% endblock %} diff --git a/badger_multiplayer/urls.py b/badger_multiplayer/urls.py index a13f2f4..c139a39 100644 --- a/badger_multiplayer/urls.py +++ b/badger_multiplayer/urls.py @@ -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[^/]+)/edit$', 'edit', + url(r'^detail/(?P[^/]+);nominate$', 'nominate_for', + name='badger_multiplayer.nominate_for'), + url(r'^detail/(?P[^/]+);edit$', 'edit', name='badger_multiplayer.badge_edit'), + url(r'^detail/(?P[^/]+)/nominations/(?P[^/]+)/?$', 'nomination_detail', + name='badger.nomination_detail'), url(r'^feeds/(?P[^/]+)/badges/?$', BadgesRecentFeed(), name="badger_multiplayer.feeds.badges_recent"), diff --git a/badger_multiplayer/views.py b/badger_multiplayer/views.py index 70039da..b58316c 100644 --- a/badger_multiplayer/views.py +++ b/badger_multiplayer/views.py @@ -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))