Skip to content

Commit e84a4cf

Browse files
committed
Added reCAPTCHA to new question form
1 parent 563a122 commit e84a4cf

4 files changed

Lines changed: 108 additions & 33 deletions

File tree

forums/settings.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@
1818
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
1919

2020

21+
# reCAPTCHA Settings
22+
RECAPTCHA_SITE_KEY = os.getenv("RECAPTCHA_SITE_KEY")
23+
RECAPTCHA_SECRET_KEY = os.getenv("RECAPTCHA_SECRET_KEY")
24+
2125
# Quick-start development settings - unsuitable for production
2226
# See https://docs.djangoproject.com/en/1.11/howto/deployment/checklist/
2327

@@ -108,8 +112,8 @@
108112
'ENGINE': 'django.db.backends.mysql', # Add 'postgresql_psycopg2', 'mysql', 'sqlite3' or 'oracle'.
109113
'NAME': os.getenv("SPOKEN_DB"), # Or path to database file if using sqlite3.
110114
# The following settings are not used with sqlite3:
111-
'USER': os.getenv("DB_USER"),
112-
'PASSWORD': os.getenv("DB_PASSWORD"),
115+
'USER': os.getenv("SPOKEN_DB_USER"),
116+
'PASSWORD': os.getenv("SPOKEN_DB_PASSWORD"),
113117
# Empty for localhost through domain sockets or '127.0.0.1' for localhost through TCP.
114118
'HOST': '',
115119
'PORT': '', # Set to empty string for default.
@@ -150,7 +154,7 @@
150154

151155
LANGUAGE_CODE = 'en-us'
152156

153-
TIME_ZONE = 'Asia/Calcutta'
157+
TIME_ZONE = 'Asia/Kolkata'
154158

155159
USE_I18N = True
156160

requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,4 @@ django-debug-toolbar==1.4
1515
python-dotenv==0.10.3
1616
nltk==3.5
1717
sklearn==0.0
18+
requests>=2.25.0

static/website/templates/new-question.html

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,13 @@ <h4>
5454
<label for="id_body">Question:</label>
5555
{% render_field form.body class+="form-control" %}
5656
</div>
57+
58+
<!-- reCAPTCHA widget, only for users without a role -->
59+
{% if require_recaptcha %}
60+
<div class="form-group" style="margin-top: 20px; margin-bottom: 15px;">
61+
<div class="g-recaptcha" data-sitekey="{{ recaptcha_site_key }}"></div>
62+
</div>
63+
{% endif %}
5764
</div>
5865
</div>
5966
<input class="btn btn-success" type="submit" value="Submit Question">
@@ -76,6 +83,7 @@ <h4 class="modal-title" id="myModalLabel">Similar Questions</h4>
7683
</div><!-- /.modal -->
7784

7885
<script src="{% static 'website/js/nicEdit.js' %}" type="text/javascript"></script>
86+
<script src="https://www.google.com/recaptcha/api.js" async defer></script>
7987
<script type="text/javascript">
8088
bkLib.onDomLoaded(function() {
8189
new nicEditor({

website/views.py

Lines changed: 92 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import json
2+
import requests
23

34
from django.http import HttpResponse, HttpResponseRedirect, HttpResponseForbidden
45
from django.shortcuts import render, get_object_or_404
@@ -318,6 +319,50 @@ def filter(request, category=None, tutorial=None, minute_range=None, second_rang
318319
def new_question(request):
319320
context = {}
320321
if request.method == 'POST':
322+
# check if user has a role
323+
user_has_role = request.user.is_authenticated and request.user.groups.exists()
324+
325+
# only require captcha for users without a role
326+
if not user_has_role:
327+
328+
recaptcha_response = request.POST.get('g-recaptcha-response', '')
329+
330+
if not recaptcha_response:
331+
messages.error(request, "Please complete the reCAPTCHA verification.")
332+
form = NewQuestionForm(request.POST)
333+
context['form'] = form
334+
context['recaptcha_site_key'] = settings.RECAPTCHA_SITE_KEY
335+
context['require_recaptcha'] = True
336+
return render(request, 'website/templates/new-question.html', context)
337+
338+
# verify with google
339+
recaptcha_verification_url = "https://www.google.com/recaptcha/api/siteverify"
340+
recaptcha_data = {
341+
'secret': settings.RECAPTCHA_SECRET_KEY,
342+
'response': recaptcha_response
343+
}
344+
345+
try:
346+
recaptcha_result = requests.post(recaptcha_verification_url, data=recaptcha_data, timeout=5)
347+
recaptcha_result.raise_for_status()
348+
recaptcha_json = recaptcha_result.json()
349+
except requests.RequestException as e:
350+
messages.error(request, "Error verifying reCAPTCHA. Please try again.")
351+
form = NewQuestionForm(request.POST)
352+
context['form'] = form
353+
context['recaptcha_site_key'] = settings.RECAPTCHA_SITE_KEY
354+
context['require_recaptcha'] = True
355+
return render(request, 'website/templates/new-question.html', context)
356+
357+
# check if verification was successful
358+
if not recaptcha_json.get('success', False):
359+
messages.error(request, "reCAPTCHA verification failed. Please try again.")
360+
form = NewQuestionForm(request.POST)
361+
context['form'] = form
362+
context['recaptcha_site_key'] = settings.RECAPTCHA_SITE_KEY
363+
context['require_recaptcha'] = True
364+
return render(request, 'website/templates/new-question.html', context)
365+
321366
form = NewQuestionForm(request.POST)
322367
if form.is_valid():
323368
cleaned_data = form.cleaned_data
@@ -331,35 +376,51 @@ def new_question(request):
331376
question.body = cleaned_data['body']
332377
question.views = 1
333378
question.save()
379+
# Run spam detection
380+
action = handle_spam(question, request.user)
381+
382+
if action == "AUTO_DELETE":
383+
messages.error(request, " Your question is being marked as spam and your account has been deactivated.")
384+
user_logout(request)
385+
return HttpResponseRedirect('/')
386+
387+
elif action == "FLAGGED":
388+
messages.warning(request, " Your question is pending moderator review.")
389+
# Don’t send email for flagged content
390+
return HttpResponseRedirect('/')
391+
392+
else: # APPROVED
393+
394+
subject = 'New Forum Question'
395+
message = f"""
396+
The following new question has been posted in the Spoken Tutorial Forum: <br>
397+
Title: <b>{question.title}</b><br>
398+
Category: <b>{question.category}</b><br>
399+
Tutorial: <b>{question.tutorial}</b><br>
400+
Link: <a href="http://forums.spoken-tutorial.org/question/{question.id}">
401+
http://forums.spoken-tutorial.org/question/{question.id}
402+
</a><br>
403+
Question: <b>{question.body}</b><br>
404+
"""
405+
email = EmailMultiAlternatives(
406+
subject, '', 'forums',
407+
['team@spoken-tutorial.org', 'team@fossee.in'],
408+
headers={"Content-type": "text/html;charset=iso-8859-1"}
409+
)
410+
email.attach_alternative(message, "text/html")
411+
email.send(fail_silently=True)
412+
return HttpResponseRedirect('/')
334413

335-
# Sending email when a new question is asked
336-
subject = 'New Forum Question'
337-
message = """
338-
The following new question has been posted in the Spoken Tutorial Forum: <br>
339-
Title: <b>{0}</b><br>
340-
Category: <b>{1}</b><br>
341-
Tutorial: <b>{2}</b><br>
342-
Link: <a href="{3}">{3}</a><br>
343-
Question: <b>{4}</b><br>
344-
""".format(
345-
question.title,
346-
question.category,
347-
question.tutorial,
348-
'http://forums.spoken-tutorial.org/question/' + str(question.id),
349-
question.body
350-
)
351-
email = EmailMultiAlternatives(
352-
subject, '', 'forums',
353-
['team@spoken-tutorial.org', 'team@fossee.in'],
354-
headers={"Content-type": "text/html;charset=iso-8859-1"}
355-
)
356-
email.attach_alternative(message, "text/html")
357-
email.send(fail_silently=True)
358-
# End of email send
414+
# If form not valid -> re-render with errors
415+
context['form'] = form
416+
context['recaptcha_site_key'] = settings.RECAPTCHA_SITE_KEY
417+
# check if user needs to complete captcha
418+
user_has_role = request.user.is_authenticated and request.user.groups.exists()
419+
context['require_recaptcha'] = not user_has_role
420+
return render(request, 'website/templates/new-question.html', context)
359421

360-
return HttpResponseRedirect('/')
361422
else:
362-
# get values from URL.
423+
# GET request -> render empty form
363424
category = request.GET.get('category', None)
364425
tutorial = request.GET.get('tutorial', None)
365426
minute_range = request.GET.get('minute_range', None)
@@ -368,10 +429,11 @@ def new_question(request):
368429
form = NewQuestionForm(category=category, tutorial=tutorial,
369430
minute_range=minute_range, second_range=second_range)
370431
context['category'] = category
371-
372-
context['form'] = form
373-
context.update(csrf(request))
374-
return render(request, 'website/templates/new-question.html', context)
432+
context['recaptcha_site_key'] = settings.RECAPTCHA_SITE_KEY
433+
# check if user needs to complete captcha
434+
user_has_role = request.user.is_authenticated and request.user.groups.exists()
435+
context['require_recaptcha'] = not user_has_role
436+
return render(request, 'website/templates/new-question.html', context)
375437

376438
# Notification Section
377439

0 commit comments

Comments
 (0)