55from django .contrib import messages
66from django .contrib .auth import get_user_model
77from django .contrib .auth .decorators import login_required
8+ from django .http import Http404
89from django .shortcuts import redirect , render
910
1011from ..auth .decorators import eighth_admin_required
1112from ..bus .models import Route
13+ from ..notifications .tasks import email_send_task
1214from ..users .models import Email
1315from .forms import BusRouteForm , DarkModeForm , EmailFormset , NotificationOptionsForm , PreferredPictureForm , PrivacyOptionsForm
16+ from .models import UnverifiedEmail
1417
1518# from .forms import (BusRouteForm, DarkModeForm, EmailFormset, NotificationOptionsForm, PhoneFormset, PreferredPictureForm, PrivacyOptionsForm,
1619# WebsiteFormset)
@@ -52,6 +55,18 @@ def get_personal_info(user):
5255 return personal_info , num_fields
5356
5457
58+ def send_verification_email (user , email ):
59+ email_link = UnverifiedEmail (user = user , email = email )
60+ email_link .save ()
61+
62+ data = {"email_address" : email .address , "verification_link" : f"ion.tjhsst.edu/preferences/verify_email/{ email_link .verification_token } /" }
63+ headers = {
"From" :
"ION <[email protected] >" ,
"Reply-To" :
"[email protected] " }
64+
65+ email_send_task .delay (
66+ "preferences/email/verify_email.txt" , "preferences/email/verify_email.html" , data , "ION Email Verification" , [email .address ], headers
67+ )
68+
69+
5570def save_personal_info (request , user ):
5671 # phone_formset = PhoneFormset(request.POST, instance=user, prefix="pf")
5772 phone_formset = None
@@ -68,7 +83,23 @@ def save_personal_info(request, user):
6883 # else:
6984 # errors.append("Could not set phone numbers.")
7085 if email_formset .is_valid ():
71- email_formset .save ()
86+ new_emails = email_formset .save (commit = False )
87+
88+ # Manually handle saving the formset so we can flag new emails as unverified.
89+ for email in new_emails :
90+ if email ._state .adding :
91+ email .verified = False
92+ email .save ()
93+ send_verification_email (user , email )
94+ messages .success (
95+ request , f"Successfully sent verification email to '{ email .address } '. This address will be deleted if it's not verified in 6 hours."
96+ )
97+
98+ for deleted_email in email_formset .deleted_objects :
99+ try :
100+ deleted_email .delete ()
101+ except deleted_email .DoesNotExist :
102+ pass
72103 else :
73104 for error in email_formset .errors :
74105 if isinstance (error .get ("address" ), list ):
@@ -207,6 +238,13 @@ def save_notification_options(request, user):
207238 if field in notification_options and notification_options [field ] == fields [field ]:
208239 pass
209240 else :
241+ # Users should only be able to set verified emails as their primary email.
242+ if field == "primary_email" and fields [field ] is not None :
243+ email = Email .objects .filter (user = user , address = fields [field ]).first ()
244+ if not email .verified :
245+ messages .error (request , "You may only set verified emails as your primary email." )
246+ continue
247+
210248 setattr (user , field , fields [field ])
211249 user .save ()
212250 try :
@@ -290,6 +328,28 @@ def save_dark_mode_settings(request, user):
290328 return dark_mode_form
291329
292330
331+ @login_required
332+ def verify_email_view (request , email_uuid ):
333+ """ "Verify the UUID associated with the unverified email."""
334+ user = request .user
335+
336+ unverified_email = UnverifiedEmail .objects .filter (verification_token = email_uuid , user = user ).first ()
337+
338+ # If the uuid isn't found, it will return the default 404 form.
339+ if unverified_email is None :
340+ raise Http404
341+
342+ verified_mail = unverified_email .email
343+ verified_mail .verified = True
344+
345+ verified_mail .save ()
346+ unverified_email .delete ()
347+
348+ context = {"email" : verified_mail }
349+
350+ return render (request , "preferences/verify_email.html" , context )
351+
352+
293353@login_required
294354def preferences_view (request ):
295355 """View and process updates to the preferences page."""
@@ -331,6 +391,13 @@ def preferences_view(request):
331391 email_formset = EmailFormset (instance = user , prefix = "ef" )
332392 # website_formset = WebsiteFormset(instance=user, prefix="wf")
333393
394+ # Flag emails as verified or unverified for templating.
395+ for form in email_formset :
396+ if form .instance .pk :
397+ form .verified = form .instance .verified
398+ else :
399+ form .verified = None
400+
334401 if user .is_student :
335402 preferred_pic = get_preferred_pic (user )
336403 bus_route = get_bus_route (user )
0 commit comments