66from django .contrib .auth import get_user_model
77from django .contrib .auth .decorators import login_required
88from django .shortcuts import redirect , render
9+ from django .urls import reverse
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,20 @@ def get_personal_info(user):
5255 return personal_info , num_fields
5356
5457
58+ def send_verification_email (request , user , email ):
59+ email_link = UnverifiedEmail (user = user , email = email )
60+ email_link .save ()
61+
62+ verification_link = request .build_absolute_uri (reverse ("verify_email" , args = [email_link .verification_token ]))
63+ base_url = request .build_absolute_uri (reverse ("index" ))
64+ data = {"verification_link" : verification_link , "base_url" : base_url }
65+ headers = {
"From" :
"Ion <[email protected] >" }
66+
67+ email_send_task .delay (
68+ "preferences/email/verify_email.txt" , "preferences/email/verify_email.html" , data , "Email Verification" , [email .address ], headers
69+ )
70+
71+
5572def save_personal_info (request , user ):
5673 # phone_formset = PhoneFormset(request.POST, instance=user, prefix="pf")
5774 phone_formset = None
@@ -68,7 +85,24 @@ def save_personal_info(request, user):
6885 # else:
6986 # errors.append("Could not set phone numbers.")
7087 if email_formset .is_valid ():
71- email_formset .save ()
88+ new_emails = email_formset .save (commit = False )
89+
90+ # Manually handle saving the formset so we can flag new emails as unverified.
91+ for email in new_emails :
92+ if email ._state .adding :
93+ email .verified = False
94+ email .save ()
95+ send_verification_email (request , user , email )
96+ messages .success (
97+ request ,
98+ f"Successfully sent verification email to '{ email .address } '. The link will expire in { settings .UNVERIFIED_EMAIL_EXPIRE_HOURS } hours." ,
99+ )
100+
101+ for deleted_email in email_formset .deleted_objects :
102+ try :
103+ deleted_email .delete ()
104+ except deleted_email .DoesNotExist :
105+ pass
72106 else :
73107 for error in email_formset .errors :
74108 if isinstance (error .get ("address" ), list ):
@@ -207,6 +241,13 @@ def save_notification_options(request, user):
207241 if field in notification_options and notification_options [field ] == fields [field ]:
208242 pass
209243 else :
244+ # Users should only be able to set verified emails as their primary email.
245+ if field == "primary_email" and fields [field ] is not None :
246+ email = Email .objects .filter (user = user , address = fields [field ]).first ()
247+ if not email .verified :
248+ messages .error (request , "You may only set verified emails as your primary email." )
249+ continue
250+
210251 setattr (user , field , fields [field ])
211252 user .save ()
212253 try :
@@ -290,6 +331,28 @@ def save_dark_mode_settings(request, user):
290331 return dark_mode_form
291332
292333
334+ @login_required
335+ def verify_email_view (request , email_uuid ):
336+ """ "Verify the UUID associated with the unverified email."""
337+ user = request .user
338+
339+ unverified_email = UnverifiedEmail .objects .filter (verification_token = email_uuid , user = user ).first ()
340+
341+ # If the uuid isn't found or link is expired, return a error message.
342+ if unverified_email is None or unverified_email .is_expired ():
343+ messages .error (request , "Could not verify email, the link was either expired or invalid." )
344+ return redirect ("preferences" )
345+
346+ verified_mail = unverified_email .email
347+ verified_mail .verified = True
348+
349+ verified_mail .save ()
350+ unverified_email .delete ()
351+
352+ messages .success (request , f"Successfully verified '{ verified_mail .address } '. You can add it as a primary email now." )
353+ return redirect ("preferences" )
354+
355+
293356@login_required
294357def preferences_view (request ):
295358 """View and process updates to the preferences page."""
@@ -331,6 +394,13 @@ def preferences_view(request):
331394 email_formset = EmailFormset (instance = user , prefix = "ef" )
332395 # website_formset = WebsiteFormset(instance=user, prefix="wf")
333396
397+ # Flag emails as verified or unverified for templating.
398+ for form in email_formset :
399+ if form .instance .pk :
400+ form .verified = form .instance .verified
401+ else :
402+ form .verified = None
403+
334404 if user .is_student :
335405 preferred_pic = get_preferred_pic (user )
336406 bus_route = get_bus_route (user )
0 commit comments