Code comments are potentially mine.
1. def register in apps/users/views.py
@anonymous_csrf
def register(request):
if request.user.is_authenticated():
messages.info(request, _("You are already logged in to an account."))
form = None
elif request.method == 'POST':
form = forms.UserRegisterForm(request.POST) # Always have recaptcha
if form.is_valid(): # is_valid() does all the form clean()
... [save form stuff] ...
else:
form = forms.UserRegisterForm()
return jingo.render(request, 'users/register.html', {'form': form, })
2. UserRegisterForm has ReCaptchaField. File: apps/users/forms.py
import captcha.fields
class UserRegisterForm(happyforms.ModelForm, PasswordMixin):
passwords ...
recaptcha = captcha.fields.ReCaptchaField()
... irrelevent stuff ...
3. captcha.fields.ReCaptchaField() in zamboni/vendors (not shown on zamboni github), but it's on Mozilla's django-recaptcha
from django.conf import settings
from django import forms
from django.utils.encoding import smart_unicode
from django.utils.translation import ugettext_lazy as _
from recaptcha.client import captcha
from captcha.widgets import ReCaptcha
class ReCaptchaField(forms.CharField):
default_error_messages = {
'captcha_invalid': _(u'Invalid captcha')
}
def __init__(self, *args, **kwargs):
self.widget = ReCaptcha
self.required = True
super(ReCaptchaField, self).__init__(*args, **kwargs)
def clean(self, values):
super(ReCaptchaField, self).clean(values[1])
recaptcha_challenge_value = smart_unicode(values[0])
recaptcha_response_value = smart_unicode(values[1])
check_captcha = captcha.submit(recaptcha_challenge_value,
recaptcha_response_value, settings.RECAPTCHA_PRIVATE_KEY, {})
if not check_captcha.is_valid:
raise forms.util.ValidationError(
self.error_messages['captcha_invalid'])
return values[0]
Which, btw is exactly what I have for my ReCaptchaField. The ReCaptcha widget will ultimately introduce an in-body javascript.
Click to read more about ReCaptcha and In-Line Javascript / CSP
So the only difference in reCaptcha is how it's displayed on the html page. Let's investigate.
4. Register page template: apps/users/templates/users/register.html, taken from step 1 views.py
{% block js %}{% include("amo/recaptcha_js.html") %}{% endblock %}
...
{% if settings.RECAPTCHA_PRIVATE_KEY %}
{{ recaptcha(form) }}
{% else %}
<p>
Welcome Robots, ReCaptcha has been disabled for your convenience.
Spam at Wil.
</p>
{% endif %}
The apps/amo/templates/amo/recaptcha_js.html has:{% if request.user.is_anonymous() %}
<script type="text/javascript" src="{{ settings.RECAPTCHA_URL }}"></script>
{% endif %}
where # in settings.py
RECAPTCHA_PUBLIC_KEY = "blah"
RECAPTCHA_PRIVATE_KEY = "blah"
RECAPTCHA_URL = ('https://www.google.com/recaptcha/api/challenge?k=%s' %
RECAPTCHA_PUBLIC_KEY)
Unless you have the private key (which bots don't), you can see the recaptcha form.
5. def recaptcha() in apps/amo/helpers.py
Read about the inclusion_tag
@register.inclusion_tag('amo/recaptcha.html')
@jinja2.contextfunction
def recaptcha(context, form):
d = dict(context.items())
d.update(form=form)
return d
6. recaptcha.html lives in apps/amo/templates/amo/recaptcha.hhtml"
{% from 'includes/forms.html' import required %}
<label for="recaptcha_response_field">
{{ _('Are you human?') }} {{ required() }}
</label>
{% trans %}
<p>
Please enter <strong>both words</strong> below,
<strong>separated by a space</strong>.
</p>
<p>
If this is hard to read, you can
<a href="#" id="recaptcha_different">try different words</a> or
<a href="#" id="recaptcha_audio">listen to something</a> instead.
</p>
{% endtrans %}
<div id="recaptcha_image"></div>
<p>
<input type="text" name="recaptcha_response_field"
id="recaptcha_response_field" size="30" />
</p>
<p><a href="#" id="recaptcha_help">{{ _("What's this?") }}</a></p>
{{ form.recaptcha.errors }}
7. div ids link to function in javascript here: media/js/zamboni/users.js
// Recaptcha
var RecaptchaOptions = { theme : 'custom' };
$('#recaptcha_different').click(function(e) {
e.preventDefault();
Recaptcha.reload();
});
$('#recaptcha_audio').click(function(e) {
e.preventDefault();
Recaptcha.switch_type('audio');
});
$('#recaptcha_help').click(function(e) {
e.preventDefault();
Recaptcha.showhelp();
});
These Recaptcha's functions are defined in Google's recaptcha.
No comments:
Post a Comment