Source code for fluxscoreboard.forms._fields
# -*- coding: utf-8 -*-
from __future__ import unicode_literals, absolute_import, print_function
from fluxscoreboard.forms._validators import (RecaptchaValidator,
greater_zero_if_set)
from pyramid.threadlocal import get_current_request
from pytz import utc
from wtforms.fields.core import Field
from wtforms.fields.html5 import IntegerField, DateTimeField
from wtforms.fields.simple import FileField
from wtforms.widgets.core import FileInput, HTMLString, html_params, Input
from wtforms.widgets.html5 import NumberInput
__doc__ = """
This module contains some custom fields and widgets.
"""
[docs]class AvatarWidget(FileInput):
"""
A widget that renders the current avatar above the form to upload a new
one.
"""
def __call__(self, field, **kwargs):
out = []
request = get_current_request()
filename = request.team.avatar_filename
if filename:
fpath = request.route_url('avatar', avatar=filename)
out.append('<img class="avatar-large" src="%s" />' % fpath)
out.append('<input id="delete-avatar" type="submit" '
'class="btn btn-danger btn-small" value="Delete Avatar"'
' name="delete-avatar" />')
out.append(FileInput.__call__(self, field, **kwargs))
return HTMLString(''.join(out))
[docs]class AvatarField(FileField):
"""
An avatar upload field with a display of an existing avatar.
"""
widget = AvatarWidget()
def process(self, formdata, *args, **kw):
if formdata.getlist("delete-avatar"):
self.delete = True
else:
self.delete = False
return FileField.process(self, formdata, *args, **kw)
[docs]class ButtonWidget(object):
"""
.. todo::
Document
"""
html_params = staticmethod(html_params)
def __init__(self, *args, **kwargs):
pass
def __call__(self, field, **kwargs):
kwargs.setdefault('id', field.id)
val = field.label.text
out = ('<button %s>%s</button>' %
(self.html_params(name=field.name, **kwargs),
val)
)
return HTMLString(out)
[docs]class IntegerOrEvaluatedField(IntegerField):
"""
A field that is basically an integer but with the added exception that,
if the challenge is manual, it will contain the value ``"evaulauted"``
which is also valid. May also be empty.
"""
def process_formdata(self, valuelist):
[value] = valuelist
if valuelist:
if value == 'evaluated':
self.data = None
return True
elif not value:
self.data = None
return True
else:
try:
self.data = int(value)
except ValueError:
self.data = None
raise ValueError(self.gettext('Not a valid integer value'))
[docs]class IntegerOrNoneField(IntegerField):
def process_formdata(self, valuelist):
if valuelist and valuelist[0]:
return IntegerField.process_formdata(self, valuelist)
else:
self.data = None
return True
[docs]class BootstrapWidget(Input):
def __init__(self, input_type=None, group_before=None, group_after=None,
default_classes=None):
self.group_before = group_before or []
self.group_after = group_after or []
self.default_classes = default_classes or []
Input.__init__(self, input_type=input_type)
def __call__(self, field, **kwargs):
html = []
html.append('<div class="input-group">')
for text in self.group_before:
html.append('<span class="input-group-addon">%s</span>' % text)
classes = kwargs.get("class_", "").split(" ")
for class_ in self.default_classes:
classes.append(class_)
kwargs["class_"] = " ".join(classes)
html.append(Input.__call__(self, field, **kwargs))
for text in self.group_after:
html.append('<span class="input-group-addon">%s</span>' % text)
html.append('</div>')
return HTMLString("".join(html))
[docs]def team_size_field():
return IntegerOrNoneField("Team Size",
description=("For statistical purposes we would "
"like to know how many you are. "
"There is no limitation on the number "
"of people each team may bring, we "
"just like to know the sizes of "
"teams."),
widget=BootstrapWidget(
'number',
group_after=['Members'],
default_classes=['text-right']),
validators=[greater_zero_if_set],
)
[docs]class RecaptchaWidget(object):
"""
RecaptchaValidator widget that displays HTML depending on security status.
"""
RECAPTCHA_HTML = u"""
<script type="text/javascript"
src="%(protocol)s://www.google.com/recaptcha/api/challenge?k=%(public_key)s">
</script>
<noscript>
<iframe
src="%(protocol)s://www.google.com/recaptcha/api/noscript?k=%(public_key)s"
height="300" width="500" frameborder="0"></iframe><br>
<textarea name="recaptcha_challenge_field" rows="3" cols="40">
</textarea>
<input type="hidden" name="recaptcha_response_field"
value="manual_challenge">
</noscript>
"""
def __call__(self, field, **kwargs):
html = self.RECAPTCHA_HTML % {
'protocol': 'https' if field.secure else 'http',
'public_key': field.public_key
}
return HTMLString(html)
[docs]class RecaptchaField(Field):
"""Handles captcha field display and validation via reCaptcha"""
widget = RecaptchaWidget()
def __init__(self, label='', validators=None, public_key=None,
private_key=None, secure=False, http_proxy=None, **kwargs):
# Pretty useless without the RecaptchaValidator but still
# user may want to subclass it, so keep it optional
validators = validators or [RecaptchaValidator()]
super(RecaptchaField, self).__init__(label, validators, **kwargs)
if not public_key or not private_key:
raise ValueError('Both recaptcha public and private keys are '
'required.')
self.public_key = public_key
self.private_key = private_key
self.secure = secure
self.http_proxy = http_proxy
self.ip_address = None
self.challenge = None
def process(self, formdata, data={}):
"""Handles multiple formdata fields that are required for reCaptcha.
Only response field is handled as raw_data as it is the only user input
"""
self.process_errors = []
if isinstance(data, dict):
self.ip_address = data.pop('ip_address', None)
try:
self.process_data(data)
except ValueError, e:
self.process_errors.append(e.args[0])
if formdata is not None:
# Developer must supply ip_address directly so throw a
# non-validation exception if it's not present
if not self.ip_address:
raise ValueError('IP address is required.')
try:
# These fields are coming from the outside so keep them
# inside the usual process
challenge = formdata.getlist('recaptcha_challenge_field')
if not challenge:
raise ValueError(self.gettext('Challenge data is '
'required.'))
self.challenge = challenge[0]
# Pass user input as the raw_data
self.raw_data = formdata.getlist('recaptcha_response_field')
self.process_formdata(self.raw_data)
except ValueError, e:
self.process_errors.append(e.args[0])
for filter_ in self.filters:
try:
self.data = filter_(self.data)
except ValueError, e:
self.process_errors.append(e.args[0])
[docs]class TZDateTimeField(DateTimeField):
def process_formdata(self, valuelist):
DateTimeField.process_formdata(self, valuelist)
if self.data:
self.data = utc.localize(self.data)