aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatthew Lemon <y@yulqen.org>2024-12-03 17:10:23 +0000
committerMatthew Lemon <y@yulqen.org>2024-12-03 17:10:23 +0000
commit3bcd728b0bd37c95865205a75ffbddfb2e086f90 (patch)
tree181a79c8e8b156509bfe03c34fd8ceff57382b20
parent38f7a88a15f1278df6a44242e2e22814914beddb (diff)
First cut at email verification
-rw-r--r--alphabetlearning/payments/admin.py10
-rw-r--r--alphabetlearning/payments/migrations/0010_pendingemailverification.py26
-rw-r--r--alphabetlearning/payments/migrations/0011_remove_pendingemailverification_first_name_and_more.py21
-rw-r--r--alphabetlearning/payments/models.py9
-rw-r--r--alphabetlearning/payments/urls.py4
-rw-r--r--alphabetlearning/payments/views.py95
-rw-r--r--alphabetlearning/templates/pages/home.html108
-rw-r--r--alphabetlearning/templates/payments/verification_failed.html4
-rw-r--r--alphabetlearning/templates/payments/verification_sent.html6
-rw-r--r--alphabetlearning/templates/payments/verification_success.html7
10 files changed, 209 insertions, 81 deletions
diff --git a/alphabetlearning/payments/admin.py b/alphabetlearning/payments/admin.py
index e9b70db..3980f67 100644
--- a/alphabetlearning/payments/admin.py
+++ b/alphabetlearning/payments/admin.py
@@ -1,14 +1,11 @@
from django.contrib import admin
from .models import CartItem
+from .models import EmailSignup
+from .models import PendingEmailVerification
from .models import Price
-from .models import Product
from .models import ShoppingCart
from .models import Subscription
-from .models import SubscriptionPlan
-from .models import EmailSignup
-from alphabetlearning.resources.models import Resource
-
# class PriceAdmin(admin.ModelAdmin):
# list_display =
@@ -43,7 +40,8 @@ class SubscriptionAdmin(admin.ModelAdmin):
# admin.site.register(Product, ProductAdmin)
admin.site.register(Price)
+admin.site.register(PendingEmailVerification)
admin.site.register(ShoppingCart, ShoppingCartAdmin)
admin.site.register(CartItem)
admin.site.register(Subscription, SubscriptionAdmin)
-admin.site.register(EmailSignup, SuccessEmailSignupAdmin) \ No newline at end of file
+admin.site.register(EmailSignup, SuccessEmailSignupAdmin)
diff --git a/alphabetlearning/payments/migrations/0010_pendingemailverification.py b/alphabetlearning/payments/migrations/0010_pendingemailverification.py
new file mode 100644
index 0000000..c49a2cb
--- /dev/null
+++ b/alphabetlearning/payments/migrations/0010_pendingemailverification.py
@@ -0,0 +1,26 @@
+# Generated by Django 5.0.4 on 2024-12-03 16:18
+
+import uuid
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('payments', '0009_emailsignup_alter_price_resource'),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='PendingEmailVerification',
+ fields=[
+ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('email', models.EmailField(max_length=254)),
+ ('first_name', models.CharField(max_length=100)),
+ ('last_name', models.CharField(max_length=100)),
+ ('verification_token', models.UUIDField(default=uuid.uuid4, editable=False)),
+ ('created_at', models.DateTimeField(auto_now_add=True)),
+ ('is_verified', models.BooleanField(default=False)),
+ ],
+ ),
+ ]
diff --git a/alphabetlearning/payments/migrations/0011_remove_pendingemailverification_first_name_and_more.py b/alphabetlearning/payments/migrations/0011_remove_pendingemailverification_first_name_and_more.py
new file mode 100644
index 0000000..e13f6d3
--- /dev/null
+++ b/alphabetlearning/payments/migrations/0011_remove_pendingemailverification_first_name_and_more.py
@@ -0,0 +1,21 @@
+# Generated by Django 5.0.4 on 2024-12-03 16:38
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('payments', '0010_pendingemailverification'),
+ ]
+
+ operations = [
+ migrations.RemoveField(
+ model_name='pendingemailverification',
+ name='first_name',
+ ),
+ migrations.RemoveField(
+ model_name='pendingemailverification',
+ name='last_name',
+ ),
+ ]
diff --git a/alphabetlearning/payments/models.py b/alphabetlearning/payments/models.py
index 7a8c7bb..0702427 100644
--- a/alphabetlearning/payments/models.py
+++ b/alphabetlearning/payments/models.py
@@ -1,3 +1,5 @@
+import uuid
+
from django.conf import settings
from django.core.exceptions import ValidationError
from django.db import models
@@ -5,6 +7,13 @@ from django.db import models
from alphabetlearning.resources.models import Resource
+class PendingEmailVerification(models.Model):
+ email = models.EmailField()
+ verification_token = models.UUIDField(default=uuid.uuid4, editable=False)
+ created_at = models.DateTimeField(auto_now_add=True)
+ is_verified = models.BooleanField(default=False)
+
+
class EmailSignup(models.Model):
email = models.EmailField(unique=True)
date_added = models.DateTimeField(auto_now_add=True)
diff --git a/alphabetlearning/payments/urls.py b/alphabetlearning/payments/urls.py
index 12f445d..38393e0 100644
--- a/alphabetlearning/payments/urls.py
+++ b/alphabetlearning/payments/urls.py
@@ -21,7 +21,9 @@ urlpatterns = [
path("landing/", views.ProductLandingPageView.as_view(), name="landing"),
path("delete-cart-item/<int:pk>", views. DeleteCartItem.as_view(), name="delete_cart_item"),
path("webhooks/stripe/", views.stripe_webhook, name="stripe-webhook"),
- path("email_signup/", views.email_signup, name="email_signup"),
+ # path("email_signup/", views.email_signup, name="email_signup"),
+ path("email-sign-up-verification/", views.email_signup_verification, name="email_signup_verification"),
+ path("verify-email/<str:token>/", views.verify_email, name="verify_email"),
path("success_email_signup/", views.SuccessEmailSignupView.as_view(), name="success_email_signup"),
path('privacy-policy/', privacy_policy, name='privacy_policy'), # Add this line
]
diff --git a/alphabetlearning/payments/views.py b/alphabetlearning/payments/views.py
index d0172d0..9691a9f 100644
--- a/alphabetlearning/payments/views.py
+++ b/alphabetlearning/payments/views.py
@@ -15,6 +15,7 @@ from django.views.generic import DeleteView
from django.views.generic import TemplateView
from alphabetlearning.payments.models import EmailSignup
+from alphabetlearning.payments.models import PendingEmailVerification
from alphabetlearning.resources.models import Resource
from alphabetlearning.users.models import User
@@ -43,38 +44,78 @@ from .models import ShoppingCart
stripe.api_key = settings.STRIPE_SECRET_KEY
+
class SuccessEmailSignupView(TemplateView):
template_name = "payments/success_email_signup.html"
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
- context['email'] = self.request.POST.get('email')
+ context["email"] = self.request.POST.get("email")
return context
-def email_signup(request):
- if request.method == 'POST':
- email = request.POST.get('email')
- if email:
- EmailSignup.objects.get_or_create(email=email)
- # Send email to user
- send_mail(
- "Thank you for signing up",
- "You have successfully signed up for our newsletter.",
- settings.DEFAULT_FROM_EMAIL,
- [email],
- fail_silently=False,
- )
- # Send email to admin
- admin_email = "admin@example.com" # Replace with actual admin email
- send_mail(
- "New Email Signup",
- f"A new user has signed up with the email: {email}",
- settings.DEFAULT_FROM_EMAIL,
- [admin_email],
- fail_silently=False,
- )
- return redirect(reverse('payments:success_email_signup')) # Redirect to a success page or similar
- return render(request, 'pages/home.html') # Adjust as necessary
+
+def email_signup_verification(request):
+ if request.method == "POST":
+ email = request.POST.get("email")
+
+ # Create pending verification
+ pending_verification = PendingEmailVerification.objects.create(
+ email=email,
+ )
+
+ # Generate verification URL
+ verification_url = request.build_absolute_uri(
+ reverse('payments:verify_email', args=[str(pending_verification.verification_token)])
+ )
+
+ # Send verification email
+ subject = 'Alphabet Learning - Email Verification'
+ message = f'''
+ Hello!,
+
+ You recently requested to sign up to the Alpabet Learning contract list.
+
+ Please click the following link to verify your email address:
+ {verification_url}
+
+ If you didn't request this, please ignore this email.
+
+ Best regards,
+ The Alphabet Learning Team
+ '''
+ send_mail(
+ subject,
+ message,
+ settings.DEFAULT_FROM_EMAIL,
+ [email],
+ 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 = PendingEmailVerification.objects.get(
+ verification_token=token,
+ is_verified=False
+ )
+
+ # Create the subscriber
+ EmailSignup.objects.create(
+ email=pending.email,
+ )
+
+ # Mark as verified
+ pending.is_verified = True
+ pending.save()
+
+ return render(request, 'payments/verification_success.html')
+
+ except PendingEmailVerification.DoesNotExist:
+ return render(request, 'payments/verification_failed.html')
+
def create_line_items(resources):
price_objs = [r.price_obj.first() for r in resources]
@@ -205,6 +246,7 @@ def stripe_webhook(request):
)
return HttpResponse(status=200)
+
class DeleteCartItem(DeleteView):
model = CartItem
success_url = reverse_lazy("payments:cart_detail")
@@ -219,5 +261,6 @@ class DeleteCartItem(DeleteView):
request.user.shoppingcart.delete()
return redirect("resources:resource_list")
+
def privacy_policy(request):
- return render(request, 'pages/privacy_policy.html')
+ return render(request, "pages/privacy_policy.html")
diff --git a/alphabetlearning/templates/pages/home.html b/alphabetlearning/templates/pages/home.html
index d837dbb..bda011c 100644
--- a/alphabetlearning/templates/pages/home.html
+++ b/alphabetlearning/templates/pages/home.html
@@ -4,13 +4,13 @@
<div class="row">
<div class="px-4 py-5 mt-5 mb-0">
<h1 class="display-1 fw-bold">Alphabet Learning</h1>
- <p class="display-6 mb-4 mt-4 p-4 rounded" style="color: #919191;">
- High quality educational resources focused on simple concepts and the first steps in learning.
+ <p class="display-6 mb-4 mt-4 p-4 rounded" style="color: #919191">
+ High quality educational resources focused on simple concepts
+ and the first steps in learning.
</p>
-
</div>
</div>
-
+
<hr
style="
border: none;
@@ -29,22 +29,19 @@
margin: 20px 0;
"
/>
-
-
+
<div class="row justify-content-center my-5">
<div class="col-lg-12">
- <h2 class="display-4 font-weight-bolder my-3">
- Sign up
- </h2>
+ <h2 class="display-4 font-weight-bolder my-3">Sign up</h2>
<div>
- <p class="display-6 mb-4" style="color: darkgray;">
- To be informed of the site launch and receive FREE resource credits,
- join our new customer mailing list...
+ <p class="display-6 mb-4" style="color: darkgray">
+ To be informed of the site launch and receive FREE resource
+ credits, join our new customer mailing list...
</p>
</div>
<form
method="post"
- action="{% url 'payments:email_signup' %}"
+ action="{% url 'payments:email_signup_verification' %}"
class="mb-4"
style="padding: 30px 0"
>
@@ -60,28 +57,38 @@
placeholder="Your email address..."
/>
</div>
- <button type="submit" style="background-color: red; color: white; border: none; padding: 20px 50px; border-radius: 10px; font-size: 1.5rem; font-weight: bold;">
- Submit
- </button>
+ <button
+ type="submit"
+ style="
+ background-color: red;
+ color: white;
+ border: none;
+ padding: 20px 50px;
+ border-radius: 10px;
+ font-size: 1.5rem;
+ font-weight: bold;
+ "
+ >
+ Submit
+ </button>
</div>
- <p class="text-muted text-center mt-3">
- <small
- >By joining our list, you agree to our
- <a
- href="{% url 'payments:privacy_policy' %}"
- class="text-decoration-none"
- >Privacy and Legal Notice</a
- >.</small
- >
- </p>
+ <p class="text-muted text-center mt-3">
+ <small
+ >By joining our list, you agree to our
+ <a
+ href="{% url 'payments:privacy_policy' %}"
+ class="text-decoration-none"
+ >Privacy and Legal Notice</a
+ >.</small
+ >
+ </p>
</form>
</div>
<div class="col-lg-12">
- <h2 class="display-4 font-weight-bolder my-3">
- Bonus offer!
- </h2>
- <p class="display-6 mb-4" style="color: darkgray;">
- As a thank-you, the first fifty customers who join our mailing list will receive a 50% discount once the site is live.
+ <h2 class="display-4 font-weight-bolder my-3">Bonus offer!</h2>
+ <p class="display-6 mb-4" style="color: darkgray">
+ As a thank-you, the first fifty customers who join our mailing
+ list will receive a 50% discount once the site is live.
</p>
</div>
</div>
@@ -129,31 +136,32 @@
</div>
</div>
-
<div class="row my-5 d-flex align-items-stretch">
<div class="col-md-12 d-flex">
<div class="p-4 flex-fill">
<h2 class="display-4 font-weight-bolder my-3">
Learning from A to Z
</h2>
- <p class="display-6" style="margin-bottom: 20px; color: darkgray;">
- Focused on early concepts,
- these resources are designed to be accessible to all learners.
+ <p
+ class="display-6"
+ style="margin-bottom: 20px; color: darkgray"
+ >
+ Focused on early concepts, these resources are designed to
+ be accessible to all learners.
</p>
</div>
</div>
- <div class="display-4 font-weight-bolder my-5">
- <h2 class="display-4 font-weight-bolder mb-2">
- No commitment required
- </h2>
- <p class="display-6" style="margin-bottom: 20px; color: darkgray;">
- Resources will be available to purchase individually
- without a subscription.
- </p>
- </div>
+ <div class="display-4 font-weight-bolder my-5">
+ <h2 class="display-4 font-weight-bolder mb-2">
+ No commitment required
+ </h2>
+ <p class="display-6" style="margin-bottom: 20px; color: darkgray">
+ Resources will be available to purchase individually without a
+ subscription.
+ </p>
+ </div>
</div>
-
<div class="row justify-content-center my-5">
<div class="col-md-4 mb-5 mb-md-0">
<img
@@ -177,8 +185,12 @@
/>
</div>
</div>
-<p class="display-6 mb-4 mt-4 p-4 col-12 rounded text-center" style="color: darkgray;">
- Alphabet Learning is currently in active <br>development and will be launched in Spring 2025.
-</p>
+ <p
+ class="display-6 mb-4 mt-4 p-4 col-12 rounded text-center"
+ style="color: darkgray"
+ >
+ Alphabet Learning is currently in active <br />development and will be
+ launched in Spring 2025.
+ </p>
</div>
{% endblock content %}
diff --git a/alphabetlearning/templates/payments/verification_failed.html b/alphabetlearning/templates/payments/verification_failed.html
new file mode 100644
index 0000000..804320a
--- /dev/null
+++ b/alphabetlearning/templates/payments/verification_failed.html
@@ -0,0 +1,4 @@
+{% extends "base.html" %} {% load static %}
+
+<h1>Verification Failed</h1>
+<p>Sorry, this verification link is invalid or has already been used.</p>
diff --git a/alphabetlearning/templates/payments/verification_sent.html b/alphabetlearning/templates/payments/verification_sent.html
new file mode 100644
index 0000000..1de1236
--- /dev/null
+++ b/alphabetlearning/templates/payments/verification_sent.html
@@ -0,0 +1,6 @@
+{% extends "base.html" %} {% load static %} {$ block content $}
+
+<h1>Verification Email Sent</h1>
+<p>Please check your email at {{ email }} to complete your subscription.</p>
+
+{$ endblock content $}
diff --git a/alphabetlearning/templates/payments/verification_success.html b/alphabetlearning/templates/payments/verification_success.html
new file mode 100644
index 0000000..1f8e95a
--- /dev/null
+++ b/alphabetlearning/templates/payments/verification_success.html
@@ -0,0 +1,7 @@
+{% extends "base.html" %} {% load static %}
+
+<h1>Email Verified Successfully</h1>
+<p>
+ Thank you for verifying your email. You have been added to our subscriber
+ list.
+</p>