kaputtnik has proposed merging lp:~widelands-dev/widelands-website/quiz_captcha into lp:widelands-website.
Commit message: Add a quiz captcha to https://www.widelands.org/legal_notice/ to prevent spam mails Requested reviews: Widelands Developers (widelands-dev) Related bugs: Bug #1799375 in Widelands Website: "Stop spam coming over legal_notice" https://bugs.launchpad.net/widelands-website/+bug/1799375 For more details, see: https://code.launchpad.net/~widelands-dev/widelands-website/quiz_captcha/+merge/369213 Added a new app called quiz_captcha and implemented it in https://www.widelands.org/legal_notice/ A quiz is a question/answer pair, set through the admin interface. If the answer don't match the question, a form error is triggered and a new question will be shown. Testing can be done on alpha, i have set 4 questions: https://alpha.widelands.org/legal_notice/ -- Your team Widelands Developers is requested to review the proposed merge of lp:~widelands-dev/widelands-website/quiz_captcha into lp:widelands-website.
=== modified file 'mainpage/forms.py' --- mainpage/forms.py 2019-03-03 15:17:58 +0000 +++ mainpage/forms.py 2019-06-23 18:35:44 +0000 @@ -4,7 +4,8 @@ from django import forms from django_registration.forms import RegistrationForm from nocaptcha_recaptcha.fields import NoReCaptchaField -from wlprofile.models import Profile as wlprofile +#from wlprofile.models import Profile as wlprofile +from quiz_captcha.fields import QuizField # Overwritten form to include a captcha @@ -18,3 +19,4 @@ forename = forms.CharField(max_length=80, required=False) email = forms.EmailField() inquiry = forms.CharField(widget=forms.Textarea) + quiz = QuizField() \ No newline at end of file === modified file 'mainpage/settings.py' --- mainpage/settings.py 2019-04-11 16:11:21 +0000 +++ mainpage/settings.py 2019-06-23 18:35:44 +0000 @@ -106,6 +106,7 @@ 'documentation', 'privacy_policy.apps.PrivacyPolicyConfig', 'haystack', # search engine; see option HAYSTACK_CONNECTIONS + 'quiz_captcha', # Modified 3rd party apps 'wiki.apps.WikiConfig', # This is based on wikiapp, but has some local modifications === modified file 'mainpage/templates/mainpage/legal_notice.html' --- mainpage/templates/mainpage/legal_notice.html 2019-01-24 18:03:54 +0000 +++ mainpage/templates/mainpage/legal_notice.html 2019-06-23 18:35:44 +0000 @@ -23,44 +23,45 @@ <ul> <li>E-Mail Holger Rapp: sirver(at)gmx.de</li> <li>Contact form. Using this form sends E-Mails to following person(s): - <ul> - {% for name, recipient in inquiry_recipients %} - <li>{{ name }}: {{ recipient }} </li> - {% endfor %} - </ul> + <ul> + {% for name, recipient in inquiry_recipients %} + <li>{{ name }}: {{ recipient }} </li> + {% endfor %} + </ul> - <form action="/legal_notice/" method="post">{% csrf_token %} - <table> - <tr> - <td><label for="id_forename">First name (optional): </label></td> - <td><input id="id_forename" type="text" name="forename" maxlength="80"></td> - </tr> - <tr> - <td><label for="id_surname">Last name (optional): </label></td> - <td><input id="id_surname" type="text" name="surname" maxlength="80"></td> - </tr> - <tr> - <td><label for="id_email">Email:</label></td> - <td><input type="email" name="email" id="id_email" required> - {% for error in form.email.errors %} - <span class="errormessage">{{ error }}</span> - {% endfor %} - </td> - </tr> - <tr> - <td><label for="id_inquiry">Inquiry: </label>{{ form.inquiry.errors }}</td> - <td><textarea id="id_inquiry" rows="10" cols="40" name="inquiry" required></textarea> - {% for error in form.inquiry.errors %} - <span class="errormessage">{{ error }}</span> - {% endfor %} - </td> - </tr> - <tr> - <td></td> - <td><input type="submit" value="Submit"></td> - </tr> - </table> - </form> + <form action="/legal_notice/" method="post"> + {% csrf_token %} + <table> + <tr> + <td><label for="id_forename">First name (optional): </label></td> + <td>{{ form.forename }}</td> + </tr> + <tr> + <td><label for="id_surname">Last name (optional): </label></td> + <td>{{ form.surname }}</td> + </tr> + <tr> + <td><label for="id_email">Email:</label></td> + <td>{{ form.email }} + {% for error in form.email.errors %} + <span class="errormessage">{{ error }}</span> + {% endfor %} + </td> + </tr> + <tr> + <td><label for="id_inquiry">Inquiry: </label></td> + <td>{{ form.inquiry }} + {% for error in form.inquiry.errors %} + <span class="errormessage">{{ error }}</span> + {% endfor %} + </td> + </tr> + </table> + {% include "quiz_captcha/quest.html" %} + <p> + <input type="submit" value="Submit"> + </p> + </form> </li> </ul> </div> === modified file 'mainpage/views.py' --- mainpage/views.py 2019-06-07 20:25:05 +0000 +++ mainpage/views.py 2019-06-23 18:35:44 +0000 @@ -19,6 +19,7 @@ def legal_notice(request): """The legal notice page to fullfill law.""" + if request.method == 'POST': form = ContactForm(request.POST) if form.is_valid(): @@ -41,8 +42,13 @@ # Redirect after POST return HttpResponseRedirect('/legal_notice_thanks/') - else: + else: # form is not valid + # assign new values to the quiz field + form.fields['quiz'].init_values() + + else: # request.GET form = ContactForm() # An unbound form + form.fields['quiz'].init_values() return render(request, 'mainpage/legal_notice.html', { 'form': form, === added directory 'quiz_captcha' === added file 'quiz_captcha/__init__.py' === added file 'quiz_captcha/admin.py' --- quiz_captcha/admin.py 1970-01-01 00:00:00 +0000 +++ quiz_captcha/admin.py 2019-06-23 18:35:44 +0000 @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.contrib import admin +from quiz_captcha.models import Quiz + +class QuizAdmin(admin.ModelAdmin): + list_display=['question', 'answer',] + +admin.site.register(Quiz, QuizAdmin) === added file 'quiz_captcha/apps.py' --- quiz_captcha/apps.py 1970-01-01 00:00:00 +0000 +++ quiz_captcha/apps.py 2019-06-23 18:35:44 +0000 @@ -0,0 +1,8 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.apps import AppConfig + + +class QuizCaptchaConfig(AppConfig): + name = 'quiz_captcha' === added file 'quiz_captcha/fields.py' --- quiz_captcha/fields.py 1970-01-01 00:00:00 +0000 +++ quiz_captcha/fields.py 2019-06-23 18:35:44 +0000 @@ -0,0 +1,42 @@ +from django import forms +from django.shortcuts import get_object_or_404 +from django.core.exceptions import ValidationError +from quiz_captcha.models import Quiz + + +class QuizField(forms.CharField): + """Extendet CharField with a Quiz object.""" + + quest = Quiz() + + def __init__(self, *args, **kwargs): + super(QuizField, self).__init__( + label=QuizField.quest.question, **kwargs) + + def init_values(self, *args, **kwargs): + """Set a new label and quiz for the field.""" + + try: + if not QuizField.quest: + # There was no quest set yet + QuizField.quest = Quiz.objects.get_random() + else: + # A quest was set, exclude it + QuizField.quest = Quiz.objects.get_random( + exclude_pk=QuizField.quest.pk) + + self.label = QuizField.quest.question + except: + # No Quizz available or other errors + # Maybe this could be better implemented by raising ValidationError + # but i couldn't figure it out + self.label = '' + + def validate(self, value): + # Run default validation, e.g. 'This field is required' + super(QuizField, self).validate(value) + + # Validate the answer against the quest object + if value != QuizField.quest.answer: + raise ValidationError( + 'Your answer was wrong. Please try to answer again.') === added directory 'quiz_captcha/migrations' === added file 'quiz_captcha/migrations/0001_initial.py' --- quiz_captcha/migrations/0001_initial.py 1970-01-01 00:00:00 +0000 +++ quiz_captcha/migrations/0001_initial.py 2019-06-23 18:35:44 +0000 @@ -0,0 +1,27 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.20 on 2019-06-20 11:03 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ] + + operations = [ + migrations.CreateModel( + name='Quiz', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('question', models.CharField(help_text="A question mark isn't applied automatically.", max_length=200)), + ('answer', models.CharField(help_text='Depending on the database collation setting, this might be case insensitive.', max_length=200)), + ], + options={ + 'verbose_name_plural': 'Quizzes', + }, + ), + ] === added file 'quiz_captcha/migrations/__init__.py' === added file 'quiz_captcha/models.py' --- quiz_captcha/models.py 1970-01-01 00:00:00 +0000 +++ quiz_captcha/models.py 2019-06-23 18:35:44 +0000 @@ -0,0 +1,44 @@ +# -*- coding: utf-8 -*- +from django.db import models +import random + + +class QuizRandomManager(models.Manager): + + def get_random(self, exclude_pk=None): + + qs = self.model.objects.all() + + if qs: + # There is at least one quizz set + if len(qs) == 1: + return qs.first() + + if exclude_pk != None: + # We don't want to show the same question again + qs = qs.exclude(pk=exclude_pk) + + ids = [quiz.pk for quiz in qs ] + i = ids[random.randint(0, len(ids)-1)] + return qs.get(pk=i) + + return None + + +class Quiz(models.Model): + question = models.CharField( + max_length = 200, + help_text = "A question mark isn't applied automatically.", + ) + answer = models.CharField( + max_length=200, + help_text = "Depending on the database collation setting, this might be case insensitive.", + ) + + class Meta: + verbose_name_plural = 'Quizzes' + + objects = QuizRandomManager() + + def __str__(self): + return self.question === added directory 'quiz_captcha/templates' === added directory 'quiz_captcha/templates/quiz_captcha' === added file 'quiz_captcha/templates/quiz_captcha/quest.html' --- quiz_captcha/templates/quiz_captcha/quest.html 1970-01-01 00:00:00 +0000 +++ quiz_captcha/templates/quiz_captcha/quest.html 2019-06-23 18:35:44 +0000 @@ -0,0 +1,14 @@ +<div class="quiz_captcha"> + {% if form.quiz.label != '' %} + <p>Please answer the question:</p> + <p>{{ form.quiz.label }} + <!--We render the input manually to prevent showing the value given prior--> + <input type="text" name="quiz" required="" id="id_quiz"> + {% for error in form.quiz.errors %} + <span class="errormessage">{{ error }}</span> + {% endfor %} + </p> + {% else %} + <p class="errormessage">Error! You may have to add a quizz in the admin interface...</p> + {% endif %} +</div> === added file 'quiz_captcha/tests.py' --- quiz_captcha/tests.py 1970-01-01 00:00:00 +0000 +++ quiz_captcha/tests.py 2019-06-23 18:35:44 +0000 @@ -0,0 +1,6 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.test import TestCase + +# Create your tests here.
_______________________________________________ Mailing list: https://launchpad.net/~widelands-dev Post to : widelands-dev@lists.launchpad.net Unsubscribe : https://launchpad.net/~widelands-dev More help : https://help.launchpad.net/ListHelp