diff --git a/.gitignore b/.gitignore index 7f7af7f0..031d78ef 100644 --- a/.gitignore +++ b/.gitignore @@ -69,4 +69,5 @@ config.yaml /py2/ /py38/ /py39/ +/py39b/ /django2/ diff --git a/rowers/forms.py b/rowers/forms.py index 3d9e3e86..4d6740c3 100644 --- a/rowers/forms.py +++ b/rowers/forms.py @@ -18,6 +18,8 @@ from django.contrib.auth.forms import UserCreationForm from django.contrib.auth.models import User from django.contrib.admin.widgets import AdminDateWidget from django.forms.widgets import SelectDateWidget, HiddenInput +from django_recaptcha.fields import ReCaptchaField +from django_recaptcha.widgets import ReCaptchaV3 from django.utils import timezone, translation from django.forms import ModelForm, Select @@ -235,11 +237,16 @@ class SearchForm(forms.Form): # simple form for Contact page. Sends email to info@rowsandall.com class EmailForm(forms.Form): - firstname = forms.CharField(max_length=255) - lastname = forms.CharField(max_length=255) + firstname = forms.CharField(max_length=255, label="First Name") + lastname = forms.CharField(max_length=255, required=False, label="Last Name") email = forms.EmailField() subject = forms.CharField(max_length=255) message = forms.CharField(widget=forms.Textarea()) + captcha = ReCaptchaField(widget=ReCaptchaV3( + attrs={ + 'required_score': 0.85, + } + )) disqualificationreasons = ( diff --git a/rowers/models.py b/rowers/models.py index 9e156657..e5da3360 100644 --- a/rowers/models.py +++ b/rowers/models.py @@ -3700,7 +3700,7 @@ class Workout(models.Model): elements = dict( date = self.date.strftime('%Y-%m-%d'), name = self.name, - distance = str(self.distance), + distance = str(self.distance)+'m', ownerfirst = self.user.user.first_name, ownerlast = self.user.user.last_name, duration = self.duration.strftime("%H:%M:%S"), @@ -3727,7 +3727,7 @@ class Workout(models.Model): if self.workouttype not in ['water','rower']: try: - stri = u'{date} {name} {distance}m {duration} {workouttype} {ownerfirst} {ownerlast}'.format( + stri = u'{date} {name} {distance} {duration} {workouttype} {ownerfirst} {ownerlast}'.format( **elements ) except ValueError: @@ -3736,7 +3736,7 @@ class Workout(models.Model): return "No workout" else: try: - stri = u'{date} {name} {distance}m {duration} {workouttype} {boattype} {ownerfirst} {ownerlast}'.format( + stri = u'{date} {name} {distance} {duration} {workouttype} {boattype} {ownerfirst} {ownerlast}'.format( **elements ) except (ValueError, AttributeError): diff --git a/rowers/templates/email.html b/rowers/templates/email.html index 86fd7b2e..000d8013 100644 --- a/rowers/templates/email.html +++ b/rowers/templates/email.html @@ -1,136 +1,80 @@ {% extends "newbase.html" %} {% block title %}Contact Us{% endblock title %} {% block main %} -

Contact us through email

    -
  • - {% if form.errors %} -

    - Please correct the error{{ form.errors|pluralize }} below. -

    - {% endif %} - - -
    {% csrf_token %} -

    - - - - - -
    - - - - - -
    - - - - - - -
    - - - -
    - - - -
    - - - - - -
    - - - -
    - -
    -

    -
    +
  • +

    Contact us through email

    + {% if form.errors %} +

    + Please correct the error{{ form.errors|pluralize }} below. +

    + {% endif %} + + +
    {% csrf_token %} +

    + + {{ form.as_table }} +
    + +

    +
  • -

    Bug reporting, feature requests

    - -

    - Bug reports and feature requests can be done through our BitBucket page. Please check on the following link if your bug or issue is a known one. Feel free to file any feature request. -

      -
    • BitBucket Issue list (click here to go report an issue or request a feature)
    • -
    -

    +

    Bug reporting, feature requests

    + +

    + Bug reports and feature requests can be done through our BitBucket page. Please check on the following link if your bug or issue is a known one. Feel free to file any feature request. +

      +
    • BitBucket Issue list (click here to go report an issue or request a feature)
    • +
    +

  • -

    Facebook Group

    +

    Facebook Group

    + +

    We run a facebook group where you can post questions and report problems, + especially if you think the wider user community benefits from the answers.

    +
      +
    • https://www.facebook.com/groups/rowsandall/
    • +
    +
  • -

    We run a facebook group where you can post questions and report problems, - especially if you think the wider user community benefits from the answers.

    -
      -
    • https://www.facebook.com/groups/rowsandall/
    • -
    - +
  • +

    Twitter

    + +

    You can also check me on Twitter: +

      +
    • https://twitter.com/rowsandall +
    + When the site is down, this is the appropriate channel to look for apologies, updates, and offer help. +

    +
  • -
  • -

    Twitter

    +
  • +

    Rowsandall s.r.o.

    + +

    Rowsandall s.r.o.
    + Nové sady 988/2
    + 602 00 Brno
    + Czech Republic
    + IČ: 070 48 572
    + DIČ: CZ 070 48 572 (Nejsme plátce DPH)
    + Datová schránka: 7897syr
    + Email: info@rowsandall.com
    + The company is registered in the business register at the + Regional Court in Brno (Společnost je zapsána v obchodním rejstříku vedeném u Krajského soudu v Brně, oddíl C, vložka 105845)
    +

    + +
  • +
+{% endblock %} -

You can also check me on Twitter: -

    -
  • https://twitter.com/rowsandall -
- When the site is down, this is the appropriate channel to look for apologies, updates, and offer help. -

- +{% block sidebar %} +{% include 'menu_help.html' %} +{% endblock %} -
  • -

    Rowsandall s.r.o.

    - -

    Rowsandall s.r.o.
    - Nové sady 988/2
    - 602 00 Brno
    - Czech Republic
    - IČ: 070 48 572
    - DIČ: CZ 070 48 572 (Nejsme plátce DPH)
    - Datová schránka: 7897syr
    - Email: info@rowsandall.com
    - The company is registered in the business register at the - Regional Court in Brno (Společnost je zapsána v obchodním rejstříku vedeném u Krajského soudu v Brně, oddíl C, vložka 105845)
    -

    - -
  • - - {% endblock %} - - {% block sidebar %} - {% include 'menu_help.html' %} - {% endblock %} - - {% block scripts %} - - - {% endblock %} diff --git a/rowers/tests/test_aworkouts.py b/rowers/tests/test_aworkouts.py index 79c139b1..c18a60bd 100644 --- a/rowers/tests/test_aworkouts.py +++ b/rowers/tests/test_aworkouts.py @@ -42,12 +42,7 @@ class ListWorkoutTest(TestCase): rowerplan='coach') self.c = Client() - self.user_workouts = WorkoutFactory.create_batch(len(workouttypes), user=self.r) - i = 0 - for workouttype in workouttypes: - self.user_workouts[i].workouttype = workouttype[0] - self.user_workouts[i].save() - i = i+1 + self.user_workouts = WorkoutFactory.create_batch(5, user=self.r) self.factory = RequestFactory() self.password = faker.word() diff --git a/rowers/tests/test_simplefunctions.py b/rowers/tests/test_simplefunctions.py index 155efefb..59753174 100644 --- a/rowers/tests/test_simplefunctions.py +++ b/rowers/tests/test_simplefunctions.py @@ -6,6 +6,7 @@ from __future__ import unicode_literals from .statements import * from django.http import Http404 +from django_recaptcha.client import RecaptchaResponse from rowers.views import get_workout @@ -69,7 +70,10 @@ class SimpleViewTest(TestCase): response = self.c.get(url) self.assertIn(response.status_code, [403, 404]) - def test_sendmail(self): + @patch("django_recaptcha.fields.client.submit") + def test_sendmail(self, mocked_submit): + mocked_submit.return_value = RecaptchaResponse(is_valid=True, extra_data={"score":0.95}) + login = self.c.login(username=self.u.username, password=self.password) self.assertTrue(login) @@ -80,18 +84,20 @@ class SimpleViewTest(TestCase): 'lastname': 'Doe', 'email': 'roosendaalsander@gmail.com', 'subject': 'testing', - 'botcheck': True, + 'captcha': 'sdsdsdsdsdsdss', + 'g-recaptcha-response': 'PASSED', 'message': faker.text()} form = EmailForm(form_data) + self.assertTrue(form.is_valid()) response = self.c.post(url, form_data, follow=True) self.assertEqual(response.status_code, 200) self.assertRedirects(response, - expected_url='/rowers/email/', + expected_url='/rowers/email/thankyou/', status_code=302, target_status_code=200) def test_getworkout(self): diff --git a/rowers/tests/testdata/testdata.tcx.gz b/rowers/tests/testdata/testdata.tcx.gz index ef0d837d..4f556076 100644 Binary files a/rowers/tests/testdata/testdata.tcx.gz and b/rowers/tests/testdata/testdata.tcx.gz differ diff --git a/rowers/tests/viewnames.csv b/rowers/tests/viewnames.csv index 245dbb17..0c19b5c5 100644 --- a/rowers/tests/viewnames.csv +++ b/rowers/tests/viewnames.csv @@ -134,7 +134,7 @@ 175,220,rower_favoritecharts_view,See favorite charts,TRUE,302,pro,200,302,pro,403,403,coach,200,403,FALSE,TRUE,FALSE,TRUE,TRUE, 176,222,workout_workflow_config2_view,configure workflow,TRUE,302,basic,200,302,basic,403,403,coach,200,403,FALSE,TRUE,FALSE,FALSE,FALSE, 177,224,workflow_default_view,resets workflow to default,TRUE,302,basic,302,302,FALSE,403,403,FALSE,302,403,FALSE,FALSE,FALSE,TRUE,TRUE, -178,225,sendmail,feedback form,TRUE,302,basic,302,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,TRUE,TRUE, +178,225,sendmail,feedback form,TRUE,200,basic,200,302,FALSE,200,302,FALSE,200,302,FALSE,FALSE,FALSE,TRUE,TRUE, 180,232,laboratory_view,lab,TRUE,302,basic,200,302,basic,403,403,coach,200,403,FALSE,TRUE,FALSE,TRUE,TRUE, 181,233,errormessage_view,not used,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, 182,237,payment_confirm_view,confirm payment,TRUE,200,basic,200,302,basic,200,302,coach,200,302,FALSE,FALSE,FALSE,FALSE,FALSE, diff --git a/rowers/urls.py b/rowers/urls.py index c1190705..a17520b9 100644 --- a/rowers/urls.py +++ b/rowers/urls.py @@ -763,7 +763,8 @@ urlpatterns = [ re_path(r'^email/send/$', views.sendmail, name='sendmail'), re_path(r'^email/thankyou/$', TemplateView.as_view(template_name='thankyou.html'), name='thankyou'), - re_path(r'^email/$', TemplateView.as_view(template_name='email.html'), name='email'), + re_path(r'^email/$', views.sendmail, name='sendmail'), +# TemplateView.as_view(template_name='email.html'), name='email'), re_path(r'^about', TemplateView.as_view( template_name='about_us.html'), name='about'), re_path(r'^brochure/$', TemplateView.as_view(template_name='brochure.html'), diff --git a/rowers/views/statements.py b/rowers/views/statements.py index 351ca027..fd7c2d99 100644 --- a/rowers/views/statements.py +++ b/rowers/views/statements.py @@ -1271,23 +1271,10 @@ def add_defaultfavorites(r): # Shows email form and sends it if submitted def sendmail(request): + form = EmailForm() if request.method == 'POST': - # test recaptcha - response_string = request.POST.get('g-recaptcha-response') - # replace below with settings - recaptcha_secret = RECAPTCHA_SITE_SECRET - url = 'https://www.google.com/recaptcha/api/siteverify' - data = { - 'secret': recaptcha_secret, - 'response': response_string, - } - response = requests.post(url, data=data, verify=True) - success = False - if response.status_code == 200: - success = response.json().get('success') - form = EmailForm(request.POST) - if form.is_valid() and success: # pragma: no cover + if form.is_valid(): # pragma: no cover firstname = form.cleaned_data['firstname'] lastname = form.cleaned_data['lastname'] email = form.cleaned_data['email'] @@ -1314,14 +1301,15 @@ def sendmail(request): request, "Something went wrong trying to send the form") return HttpResponseRedirect('/rowers/email/thankyou/') else: - if not success: - messages.error(request, 'Bots are not welcome') - else: # pragma: no cover - messages.error( - request, 'Something went wrong. Please try again') - return HttpResponseRedirect('/rowers/email/') - else: - return HttpResponseRedirect('/rowers/email/') + if "captcha" in form.errors: + messages.error(request,"Bots are not welcome") + return HttpResponseRedirect(reverse("sendmail")) + + + return render(request,'email.html', + { + 'form': form + }) def keyvalue_get_default(key, options, def_options): # pragma: no cover diff --git a/rowsandall_app/settings.py b/rowsandall_app/settings.py index 684c41ec..757c540e 100644 --- a/rowsandall_app/settings.py +++ b/rowsandall_app/settings.py @@ -82,6 +82,7 @@ INSTALLED_APPS = [ 'rules', 'taggit', 'boatmovers', + 'django_recaptcha', ] AUTHENTICATION_BACKENDS = ( @@ -611,9 +612,13 @@ except KeyError: try: RECAPTCHA_SITE_KEY = CFG['recaptcha_site_key'] RECAPTCHA_SITE_SECRET = CFG['recaptcha_site_secret'] + RECAPTCHA_PUBLIC_KEY = CFG['recaptcha_site_key'] + RECAPTCHA_PRIVATE_KEY = CFG['recaptcha_site_secret'] except KeyError: # pragma: no cover RECAPTCHA_SITE_KEY = '' RECAPTCHA_SITE_SECRET = '' + RECAPTCHA_PUBLIC_KEY = '' + RECAPTCHA_PRIVATE_KEY = '' GEOIP_PATH = STATIC_ROOT