From 7cd2fd2b75bc5597e9b2a128bf61201910be6df2 Mon Sep 17 00:00:00 2001 From: Matthew Lemon Date: Wed, 4 Dec 2024 17:11:55 +0000 Subject: Email verification - Associated changes in the .env file now allow us to test the SMTP server config - Added mine and J's email ad admins - Grab env variables from the .env file in the local.py file for testing purposes - Set an expiry of 24hrs for the validation link to work - Added an HTML version of the verification email --- alphabetlearning/payments/models.py | 7 +++ alphabetlearning/payments/views.py | 51 ++++++++++++++++++---- .../templates/payments/success_email_signup.html | 22 +++++----- config/settings/base.py | 2 +- config/settings/local.py | 15 ++++++- 5 files changed, 75 insertions(+), 22 deletions(-) diff --git a/alphabetlearning/payments/models.py b/alphabetlearning/payments/models.py index ce5c8aa..9ea9de2 100644 --- a/alphabetlearning/payments/models.py +++ b/alphabetlearning/payments/models.py @@ -1,8 +1,10 @@ import uuid +from datetime import timedelta from django.conf import settings from django.core.exceptions import ValidationError from django.db import models +from django.utils import timezone from alphabetlearning.resources.models import Resource @@ -13,6 +15,11 @@ class EmailVerification(models.Model): created_at = models.DateTimeField(auto_now_add=True) is_verified = models.BooleanField(default=False) + @property + def is_expired(self): + expiration_time = timedelta(hours=26) + return timezone.now() - self.created_at > expiration_time + def __str__(self): return f"Email verification for {self.email}" diff --git a/alphabetlearning/payments/views.py b/alphabetlearning/payments/views.py index bf4c3c9..4b6f79f 100644 --- a/alphabetlearning/payments/views.py +++ b/alphabetlearning/payments/views.py @@ -1,7 +1,7 @@ import stripe from django.conf import settings from django.contrib.auth.decorators import login_required -from django.core.mail import send_mail +from django.core.mail import send_mail, mail_admins from django.http import HttpResponse from django.http import HttpResponseBadRequest from django.shortcuts import get_object_or_404 @@ -70,14 +70,27 @@ def email_signup_verification(request): # Send verification email subject = 'Alphabet Learning - Email Verification' - message = f''' - Hello!, + html_message = f''' + + + Alphabet Learning Logo +

Hi!

+

You recently requested to sign up to the Alphabet Learning contact list.

+

Please click the following link to verify your email address within 24 hours:

+

{verification_url}

+

If you didn't request this, please ignore this email.

+

Best regards,

+

The Alphabet Learning Team

+ + + ''' + message = f''' + Hi!, - You recently requested to sign up to the Alpabet Learning contract list. + You recently requested to sign up to the Alphabet Learning contact list. - Please click the following link to verify your email address: + Please click the following link to verify your email address within 24 hours: {verification_url} - If you didn't request this, please ignore this email. Best regards, @@ -89,12 +102,24 @@ def email_signup_verification(request): settings.DEFAULT_FROM_EMAIL, [email], fail_silently=False, + html_message=html_message ) + admin_message = f''' + Joanna/Matthew, + + {email} has just signed up to the Alphabet Learning contact list. They are awaiting verification. + + I will email again if they follow through with the verification. + + Best regards, + The Alphabet Learning Server + ''' + + mail_admins(subject, admin_message, fail_silently=False) return render(request, 'payments/verification_sent.html', { 'email': email }) return render(request, "pages/home.html") # Adjust as necessary - def verify_email(request, token): try: pending = EmailVerification.objects.get( @@ -102,6 +127,9 @@ def verify_email(request, token): is_verified=False ) + if pending.is_expired: + return render(request, 'payments/verification_failed.html') + # Create the subscriber EmailSignup.objects.create( email=pending.email, @@ -111,7 +139,14 @@ def verify_email(request, token): pending.is_verified = True pending.save() - return render(request, 'payments/verification_success.html') + mail_admins( + subject=f'{pending.email} has just signed up to the Alphabet Learning contact list.', + message=f'{pending.email} has just signed up to the Alphabet Learning contact list. Their verification was successful.\n\n\n' + f'Best regards,\n\nThe Alphabet Learning Server', + fail_silently=False, + ) + + return render(request, 'payments/success_email_signup.html') except EmailVerification.DoesNotExist: return render(request, 'payments/verification_failed.html') diff --git a/alphabetlearning/templates/payments/success_email_signup.html b/alphabetlearning/templates/payments/success_email_signup.html index 5b3b9f7..c033d0a 100644 --- a/alphabetlearning/templates/payments/success_email_signup.html +++ b/alphabetlearning/templates/payments/success_email_signup.html @@ -74,8 +74,8 @@

- We are very excited to have you {{ email }} join us at Alphabet Learning! - You will be updated before we are due to go live (this is currently estimated to be Spring 2025). + We are very excited to have you {{ email }} join us at Alphabet Learning! + You will be updated before we are due to go live (this is currently estimated to be Spring 2025). Once we are live we will also send you the FREE resource credits AND a 50% discount. As you're an early bird, tell your friends so that they too could benefit from these exclusive offers.

@@ -101,7 +101,7 @@ const colors = ['#ff5258', '#ff9d3e', '#ffd850', '#2dc62b', '#197ceb', '#f19de0', '#9472eb', '#24dabc']; const container = document.querySelector('.celebration-container'); const totalLetters = 420; - + function createSpray() { for(let i = 0; i < totalLetters; i++) { setTimeout(() => { @@ -109,21 +109,21 @@ letter.className = 'letter'; letter.textContent = letters[Math.floor(Math.random() * letters.length)]; letter.style.color = colors[Math.floor(Math.random() * colors.length)]; - + // Calculate spray angle (-60 to 60 degrees) const angle = (Math.random() * 120 - 60) * (Math.PI / 180); const speed = 200 + Math.random() * 200; // Random speed for variety - + // Tighter initial spread at the source const initialSpread = Math.random() * 10 - 5; letter.style.left = `calc(50% + ${initialSpread}px)`; - + // Calculate trajectory const duration = 2 + Math.random() * 0.5; // 1-1.5s duration const xVelocity = Math.sin(angle) * speed; const yVelocity = Math.cos(angle) * speed + 200; // Adjusted to ensure letters fall below their source level const gravity = 500; // Pixels per second squared - + // Create custom animation const animation = letter.animate([ { transform: 'translate(0, 0) rotate(0deg)' }, @@ -135,15 +135,15 @@ easing: 'cubic-bezier(0.25, 0.1, 0.25, 1)', fill: 'forwards' }); - + container.appendChild(letter); - + // Remove letter after animation animation.onfinish = () => letter.remove(); }, i * 10); // Stagger creation slightly for more natural look } } - + // Create the spray once createSpray(); } @@ -154,4 +154,4 @@ {% endblock content %} {% block extra_js %} -{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/config/settings/base.py b/config/settings/base.py index 81e0f82..28e40ed 100644 --- a/config/settings/base.py +++ b/config/settings/base.py @@ -234,7 +234,7 @@ EMAIL_TIMEOUT = 5 # Django Admin URL. ADMIN_URL = "admin/" # https://docs.djangoproject.com/en/dev/ref/settings/#admins -ADMINS = [("""Matthew Lemon""", "y@yulqen.org")] +ADMINS = [("Matthew Lemon", "matt@matthewlemon.com"), ("Joanna Lemon", "joannalemon1@gmail.com")] # https://docs.djangoproject.com/en/dev/ref/settings/#managers MANAGERS = ADMINS # https://cookiecutter-django.readthedocs.io/en/latest/settings.html#other-environment-settings diff --git a/config/settings/local.py b/config/settings/local.py index 155b219..0e4b7e4 100644 --- a/config/settings/local.py +++ b/config/settings/local.py @@ -29,9 +29,20 @@ CACHES = { # EMAIL # ------------------------------------------------------------------------------ # https://docs.djangoproject.com/en/dev/ref/settings/#email-host -EMAIL_HOST = env("EMAIL_HOST", default="mailpit") +# EMAIL_HOST = env("EMAIL_HOST", default="mailpit") # https://docs.djangoproject.com/en/dev/ref/settings/#email-port -EMAIL_PORT = 1025 +# EMAIL_PORT = 1025 + +DEFAULT_FROM_EMAIL = env( + "DJANGO_DEFAULT_FROM_EMAIL", + default="alphabetlearning.online ", +) +SERVER_EMAIL = env("DJANGO_SERVER_EMAIL", default=DEFAULT_FROM_EMAIL) +EMAIL_HOST = env('EMAIL_HOST') +EMAIL_PORT = env('EMAIL_PORT') +EMAIL_HOST_USER = env('EMAIL_HOST_USER') +EMAIL_HOST_PASSWORD = env('EMAIL_HOST_PASSWORD') +EMAIL_USE_TLS = env.bool('EMAIL_USE_TLS', default=True) # WhiteNoise # ------------------------------------------------------------------------------ -- cgit v1.2.3