2011-09-29 19:21:43 +04:00
|
|
|
import logging
|
2012-04-15 06:14:34 +04:00
|
|
|
import re
|
2011-09-29 19:21:43 +04:00
|
|
|
|
|
|
|
from django.conf import settings
|
|
|
|
|
|
|
|
from django import forms
|
|
|
|
from django.db import models
|
|
|
|
from django.contrib.auth.models import User, AnonymousUser
|
2012-12-21 08:17:17 +04:00
|
|
|
from django.forms import FileField, CharField, Textarea, ValidationError
|
2012-04-15 06:14:34 +04:00
|
|
|
from django.core.validators import validate_email
|
2011-09-29 19:21:43 +04:00
|
|
|
|
|
|
|
try:
|
|
|
|
from tower import ugettext_lazy as _
|
2013-08-24 19:24:10 +04:00
|
|
|
except ImportError:
|
2011-09-29 19:21:43 +04:00
|
|
|
from django.utils.translation import ugettext_lazy as _
|
|
|
|
|
2012-12-21 08:17:17 +04:00
|
|
|
from badger.models import Award, Badge, Nomination
|
2011-09-29 19:21:43 +04:00
|
|
|
|
|
|
|
try:
|
2012-12-21 08:17:17 +04:00
|
|
|
from taggit.managers import TaggableManager
|
2013-08-24 19:24:10 +04:00
|
|
|
except ImportError:
|
2012-12-21 08:17:17 +04:00
|
|
|
TaggableManager = None
|
2011-09-29 19:21:43 +04:00
|
|
|
|
|
|
|
|
2012-04-15 06:14:34 +04:00
|
|
|
EMAIL_SEPARATOR_RE = re.compile(r'[,;\s]+')
|
|
|
|
|
|
|
|
|
2011-09-29 19:21:43 +04:00
|
|
|
class MyModelForm(forms.ModelForm):
|
|
|
|
|
|
|
|
required_css_class = "required"
|
|
|
|
error_css_class = "error"
|
|
|
|
|
|
|
|
def as_ul(self):
|
2013-08-24 18:18:52 +04:00
|
|
|
"""Returns this form rendered as HTML <li>s -- excluding the <ul></ul>.
|
|
|
|
"""
|
|
|
|
# TODO: l10n: This doesn't work for rtl languages
|
2011-09-29 19:21:43 +04:00
|
|
|
return self._html_output(
|
2013-08-24 18:18:52 +04:00
|
|
|
normal_row=(u'<li%(html_class_attr)s>%(label)s %(field)s'
|
|
|
|
'%(help_text)s%(errors)s</li>'),
|
2011-09-29 19:21:43 +04:00
|
|
|
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):
|
|
|
|
|
|
|
|
required_css_class = "required"
|
|
|
|
error_css_class = "error"
|
|
|
|
|
|
|
|
def as_ul(self):
|
2013-08-24 18:18:52 +04:00
|
|
|
"""Returns this form rendered as HTML <li>s -- excluding the <ul></ul>.
|
|
|
|
"""
|
|
|
|
# TODO: l10n: This doesn't work for rtl languages
|
2011-09-29 19:21:43 +04:00
|
|
|
return self._html_output(
|
2013-08-24 18:18:52 +04:00
|
|
|
normal_row=(u'<li%(html_class_attr)s>%(label)s %(field)s'
|
|
|
|
'%(help_text)s%(errors)s</li>'),
|
2011-09-29 19:21:43 +04:00
|
|
|
error_row=u'<li>%s</li>',
|
|
|
|
row_ender='</li>',
|
|
|
|
help_text_html=u' <p class="help">%s</p>',
|
|
|
|
errors_on_separate_row=False)
|
|
|
|
|
|
|
|
|
2012-05-21 01:19:18 +04:00
|
|
|
class MultipleItemsField(forms.Field):
|
|
|
|
"""Form field which accepts multiple text items"""
|
2012-04-15 06:14:34 +04:00
|
|
|
# Based on https://docs.djangoproject.com/en/dev/ref/forms/validation/
|
|
|
|
# #form-field-default-cleaning
|
|
|
|
widget = Textarea
|
|
|
|
|
|
|
|
def __init__(self, **kwargs):
|
2012-05-21 01:19:18 +04:00
|
|
|
self.max_items = kwargs.get('max_items', 10)
|
2013-03-18 15:14:45 +04:00
|
|
|
if 'max_items' in kwargs:
|
|
|
|
del kwargs['max_items']
|
2012-05-21 01:19:18 +04:00
|
|
|
self.separator_re = re.compile(r'[,;\s]+')
|
2013-03-18 15:14:45 +04:00
|
|
|
if 'separator_re' in kwargs:
|
|
|
|
del kwargs['separator_re']
|
2012-05-21 01:19:18 +04:00
|
|
|
super(MultipleItemsField, self).__init__(**kwargs)
|
2012-04-15 06:14:34 +04:00
|
|
|
|
|
|
|
def to_python(self, value):
|
2013-08-24 18:18:52 +04:00
|
|
|
"""Normalize data to a list of strings."""
|
2012-04-15 06:14:34 +04:00
|
|
|
if not value:
|
|
|
|
return []
|
2012-05-23 19:03:01 +04:00
|
|
|
items = self.separator_re.split(value)
|
|
|
|
return [i.strip() for i in items if i.strip()]
|
2012-05-21 01:19:18 +04:00
|
|
|
|
|
|
|
def validate_item(self, item):
|
|
|
|
return True
|
2012-04-15 06:14:34 +04:00
|
|
|
|
|
|
|
def validate(self, value):
|
2013-08-24 18:18:52 +04:00
|
|
|
"""Check if value consists only of valid items."""
|
2012-05-21 01:19:18 +04:00
|
|
|
super(MultipleItemsField, self).validate(value)
|
2012-04-15 06:14:34 +04:00
|
|
|
|
2012-05-21 01:19:18 +04:00
|
|
|
# Enforce max number of items
|
|
|
|
if len(value) > self.max_items:
|
2012-04-15 06:14:34 +04:00
|
|
|
raise ValidationError(
|
2013-11-01 18:43:51 +04:00
|
|
|
_(u'{num} items entered, only {maxnum} allowed').format(
|
2013-08-24 18:18:52 +04:00
|
|
|
num=len(value), maxnum=self.max_items))
|
2013-03-18 15:14:45 +04:00
|
|
|
|
2012-05-21 01:19:18 +04:00
|
|
|
# Validate each of the items
|
|
|
|
invalid_items = []
|
|
|
|
for item in value:
|
2012-04-15 06:14:34 +04:00
|
|
|
try:
|
2012-05-21 01:19:18 +04:00
|
|
|
self.validate_item(item)
|
2013-08-24 19:24:10 +04:00
|
|
|
except ValidationError:
|
2012-05-21 01:19:18 +04:00
|
|
|
invalid_items.append(item)
|
|
|
|
|
|
|
|
if len(invalid_items) > 0:
|
2013-08-24 18:18:52 +04:00
|
|
|
# TODO: l10n: Not all languages separate with commas
|
|
|
|
raise ValidationError(
|
2013-11-01 18:43:51 +04:00
|
|
|
_(u'These items were invalid: {itemlist}').format(
|
2013-08-24 18:18:52 +04:00
|
|
|
itemlist=u', '.join(invalid_items)))
|
2012-05-21 01:19:18 +04:00
|
|
|
|
|
|
|
|
|
|
|
class MultiEmailField(MultipleItemsField):
|
2013-03-18 15:14:45 +04:00
|
|
|
"""Form field which accepts multiple email addresses"""
|
2012-05-21 01:19:18 +04:00
|
|
|
def validate_item(self, item):
|
|
|
|
validate_email(item)
|
2012-04-15 06:14:34 +04:00
|
|
|
|
|
|
|
|
2012-04-02 05:10:12 +04:00
|
|
|
class BadgeAwardForm(MyForm):
|
|
|
|
"""Form to create either a real or deferred badge award"""
|
2011-09-29 19:21:43 +04:00
|
|
|
# TODO: Needs a captcha?
|
2012-05-21 01:19:18 +04:00
|
|
|
emails = MultiEmailField(max_items=10,
|
2013-11-01 18:43:51 +04:00
|
|
|
help_text=_(u'Enter up to 10 email addresses for badge award '
|
|
|
|
'recipients'))
|
2012-12-24 01:52:18 +04:00
|
|
|
description = CharField(
|
2012-12-24 09:18:36 +04:00
|
|
|
label='Explanation',
|
2012-12-24 01:52:18 +04:00
|
|
|
widget=Textarea, required=False,
|
2013-11-01 18:43:51 +04:00
|
|
|
help_text=_(u'Explain why this badge should be awarded'))
|
2012-04-15 01:27:54 +04:00
|
|
|
|
|
|
|
|
|
|
|
class DeferredAwardGrantForm(MyForm):
|
|
|
|
"""Form to grant a deferred badge award"""
|
|
|
|
# TODO: Needs a captcha?
|
|
|
|
email = forms.EmailField()
|
2012-05-21 01:19:18 +04:00
|
|
|
|
|
|
|
|
|
|
|
class MultipleClaimCodesField(MultipleItemsField):
|
|
|
|
"""Form field which accepts multiple DeferredAward claim codes"""
|
|
|
|
def validate_item(self, item):
|
|
|
|
from badger.models import DeferredAward
|
|
|
|
try:
|
|
|
|
DeferredAward.objects.get(claim_code=item)
|
|
|
|
return True
|
|
|
|
except DeferredAward.DoesNotExist:
|
2013-11-01 18:43:51 +04:00
|
|
|
raise ValidationError(_(u'No such claim code, {claimcode}').format(
|
2013-08-24 18:18:52 +04:00
|
|
|
claimcode=item))
|
2012-05-21 01:19:18 +04:00
|
|
|
|
|
|
|
|
|
|
|
class DeferredAwardMultipleGrantForm(MyForm):
|
|
|
|
email = forms.EmailField(
|
2013-11-01 18:43:51 +04:00
|
|
|
help_text=_(u'Email address to which claims should be granted'))
|
2012-05-21 01:19:18 +04:00
|
|
|
claim_codes = MultipleClaimCodesField(
|
2013-11-01 18:43:51 +04:00
|
|
|
help_text=_(u'Comma- or space-separated list of badge claim codes'))
|
2012-12-21 08:17:17 +04:00
|
|
|
|
|
|
|
|
|
|
|
class BadgeEditForm(MyModelForm):
|
|
|
|
|
|
|
|
class Meta:
|
|
|
|
model = Badge
|
2013-02-25 22:38:16 +04:00
|
|
|
fields = ('title', 'image', 'description',)
|
2012-12-30 08:19:06 +04:00
|
|
|
try:
|
2013-02-25 22:38:16 +04:00
|
|
|
# HACK: Add "tags" as a field only if the taggit app is available.
|
2012-12-30 08:19:06 +04:00
|
|
|
import taggit
|
2013-02-25 22:38:16 +04:00
|
|
|
fields += ('tags',)
|
2013-08-24 19:24:10 +04:00
|
|
|
except ImportError:
|
2013-02-25 22:38:16 +04:00
|
|
|
pass
|
2013-03-19 21:44:25 +04:00
|
|
|
fields += ('unique', 'nominations_accepted',
|
|
|
|
'nominations_autoapproved',)
|
2012-12-21 08:17:17 +04:00
|
|
|
|
|
|
|
required_css_class = "required"
|
|
|
|
error_css_class = "error"
|
|
|
|
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
|
|
super(BadgeEditForm, self).__init__(*args, **kwargs)
|
|
|
|
|
2013-08-24 18:18:52 +04:00
|
|
|
# TODO: l10n: Pretty sure this doesn't work for rtl languages.
|
2012-12-21 08:17:17 +04:00
|
|
|
# HACK: inject new templates into the image field, monkeypatched
|
|
|
|
# without creating a subclass
|
|
|
|
self.fields['image'].widget.template_with_clear = u'''
|
|
|
|
<p class="clear">%(clear)s
|
|
|
|
<label for="%(clear_checkbox_id)s">%(clear_checkbox_label)s</label></p>
|
|
|
|
'''
|
2013-08-24 18:18:52 +04:00
|
|
|
# TODO: l10n: Pretty sure this doesn't work for rtl languages.
|
2012-12-21 08:17:17 +04:00
|
|
|
self.fields['image'].widget.template_with_initial = u'''
|
|
|
|
<div class="clearablefileinput">
|
|
|
|
<p>%(initial_text)s: %(initial)s</p>
|
|
|
|
%(clear_template)s
|
|
|
|
<p>%(input_text)s: %(input)s</p>
|
|
|
|
</div>
|
|
|
|
'''
|
|
|
|
|
|
|
|
|
|
|
|
class BadgeNewForm(BadgeEditForm):
|
|
|
|
|
|
|
|
class Meta(BadgeEditForm.Meta):
|
|
|
|
pass
|
|
|
|
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
|
|
super(BadgeNewForm, self).__init__(*args, **kwargs)
|
|
|
|
|
|
|
|
|
|
|
|
class BadgeSubmitNominationForm(MyForm):
|
|
|
|
"""Form to submit badge nominations"""
|
|
|
|
emails = MultiEmailField(max_items=10,
|
2013-11-01 18:43:51 +04:00
|
|
|
help_text=_(
|
|
|
|
u'Enter up to 10 email addresses for badge award nominees'))
|