Skip to content

Commit

Permalink
Add profile/user update page. (#335)
Browse files Browse the repository at this point in the history
  • Loading branch information
sarahboyce authored Mar 27, 2024
1 parent 9ad8d29 commit 8144884
Show file tree
Hide file tree
Showing 6 changed files with 193 additions and 35 deletions.
39 changes: 29 additions & 10 deletions accounts/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,12 @@
from captcha.fields import ReCaptchaField
from captcha.widgets import ReCaptchaV2Checkbox
from django import forms
from django.contrib.auth.forms import UserChangeForm
from django.contrib.auth.forms import UserCreationForm

from .models import CustomUser


class CustomUserCreationForm(UserCreationForm):
captcha = ReCaptchaField(widget=ReCaptchaV2Checkbox())
email_consent = forms.BooleanField(
help_text="Required: Please check this to consent to receiving "
"administrative emails like: email verification, password reset etc.",
label="Email Consent*",
)
class BaseCustomUserForm(forms.ModelForm):
receive_newsletter = forms.BooleanField(
required=False,
help_text="Optional: Please check this to opt-in for receiving "
Expand All @@ -35,6 +28,15 @@ class CustomUserCreationForm(UserCreationForm):
"emails about upcoming program sessions. You can opt-out on "
"your profile page at anytime.",
)


class CustomUserCreationForm(BaseCustomUserForm, UserCreationForm):
captcha = ReCaptchaField(widget=ReCaptchaV2Checkbox())
email_consent = forms.BooleanField(
help_text="Required: Please check this to consent to receiving "
"administrative emails like: email verification, password reset etc.",
label="Email Consent*",
)
accepted_coc = forms.BooleanField(
required=True,
label="Accept CoC*",
Expand Down Expand Up @@ -65,12 +67,29 @@ def __init__(self, *args, **kwargs):
self.fields["email"].required = True


class CustomUserChangeForm(UserChangeForm):
class Meta:
class CustomUserChangeForm(BaseCustomUserForm):

class Meta(BaseCustomUserForm):
model = CustomUser
fields = (
"username",
"email",
"first_name",
"last_name",
"receive_program_updates",
"receive_event_updates",
"receive_newsletter",
)

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields["email"].required = True
user = kwargs["instance"]
if user.profile.email_confirmed:
self.fields["email"].help_text = (
"<p class='text-amber-600'>If you update your email you will need to reconfirm your email address.</p>"
)
else:
self.fields["email"].help_text = (
"<p class='text-amber-600'>You have not confirmed your email address.</p>"
)
33 changes: 33 additions & 0 deletions accounts/tests/test_profile_view.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ def setUpTestData(cls):
profile = ProfileFactory.create(user__username="test")
cls.user = profile.user
cls.profile_url = reverse("profile")
cls.update_profile_url = reverse("update_user")

def test_redirect_when_unauthenticated(self):
response = self.client.get(self.profile_url, follow=True)
Expand All @@ -26,3 +27,35 @@ def test_profile(self):
self.assertContains(response, "Profile Info")
self.assertContains(response, "test")
self.assertContains(response, "Jane Doe")

def test_update_profile_initial_data(self):
self.client.force_login(self.user)
response = self.client.get(self.update_profile_url)
self.assertEqual(response.status_code, 200)
self.assertContains(response, "Update Profile Info")
self.assertContains(response, "test")
self.assertContains(response, "Jane")
self.assertContains(response, "Doe")
self.assertContains(response, "You have not confirmed your email address.")

def test_update_profile(self):
self.client.force_login(self.user)
response = self.client.post(
self.update_profile_url,
data={
"username": "janedoe",
"email": "[email protected]",
"first_name": "Jane",
"last_name": "Doe",
"receive_newsletter": True,
"receive_program_updates": True,
"receive_event_updates": True,
},
follow=True,
)
self.assertEqual(response.status_code, 200)
self.assertContains(response, "Profile Info")
self.user.profile.refresh_from_db()
self.assertEqual(self.user.profile.receiving_newsletter, True)
self.assertEqual(self.user.profile.receiving_event_updates, True)
self.assertEqual(self.user.profile.receiving_program_updates, True)
2 changes: 2 additions & 0 deletions accounts/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,13 @@
from .views import ActivateAccountView
from .views import profile
from .views import SignUpView
from .views import UpdateUserView
from .views import unsubscribe

urlpatterns = [
path("", include("django.contrib.auth.urls")),
path("profile/", profile, name="profile"),
path("profile/update/", UpdateUserView.as_view(), name="update_user"),
path("signup/", SignUpView.as_view(), name="signup"),
path(
"activate/<uidb64>/<token>",
Expand Down
106 changes: 82 additions & 24 deletions accounts/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@
from django.utils.http import urlsafe_base64_decode
from django.utils.http import urlsafe_base64_encode
from django.views import View
from django.views.generic.edit import CreateView
from django.views.generic.edit import CreateView, UpdateView

from .forms import CustomUserCreationForm
from .forms import CustomUserCreationForm, CustomUserChangeForm
from .tokens import account_activation_token


Expand All @@ -47,6 +47,30 @@ def get(self, request, uidb64, token):
return redirect("signup")


def send_user_confirmation_email(request, user):
invite_link = reverse(
"activate_account",
kwargs={
"uidb64": urlsafe_base64_encode(force_bytes(user.pk)),
"token": account_activation_token.make_token(user),
},
)
unsubscribe_link = user.profile.create_unsubscribe_link()
email_dict = {
"cta_link": request.build_absolute_uri(invite_link),
"name": user.get_full_name(),
"unsubscribe_link": unsubscribe_link,
}
send_mail(
"Djangonaut Space Registration Confirmation",
render_to_string("emails/email_confirmation.txt", email_dict),
settings.DEFAULT_FROM_EMAIL,
[user.email],
html_message=render_to_string("emails/email_confirmation.html", email_dict),
fail_silently=False,
)


class SignUpView(CreateView):
form_class = CustomUserCreationForm
template_name = "registration/signup.html"
Expand Down Expand Up @@ -81,35 +105,69 @@ def form_valid(self, form):
"receiving_event_updates",
]
)
invite_link = reverse(
"activate_account",
kwargs={
"uidb64": urlsafe_base64_encode(force_bytes(user.pk)),
"token": account_activation_token.make_token(user),
},
)
unsubscribe_link = user.profile.create_unsubscribe_link()
email_dict = {
"cta_link": self.request.build_absolute_uri(invite_link),
"name": user.get_full_name(),
"unsubscribe_link": unsubscribe_link,
}
send_mail(
"Djangonaut Space Registration Confirmation",
render_to_string("emails/email_confirmation.txt", email_dict),
settings.DEFAULT_FROM_EMAIL,
[user.email],
html_message=render_to_string("emails/email_confirmation.html", email_dict),
fail_silently=False,
)
send_user_confirmation_email(self.request, user)
return super().form_valid(form)


@login_required(login_url="/accounts/login") # redirect when user is not logged in
@login_required(login_url="/accounts/login")
def profile(request):
return render(request, "registration/profile.html")


class UpdateUserView(UpdateView):
form_class = CustomUserChangeForm
template_name = "registration/update_user.html"

def get_object(self, queryset=None):
return self.request.user

def get_initial(self):
"""
Returns the initial data to use for forms on this view.
"""
initial = super().get_initial()
initial["receive_program_updates"] = (
self.request.user.profile.receiving_program_updates
)
initial["receive_event_updates"] = (
self.request.user.profile.receiving_event_updates
)
initial["receive_newsletter"] = self.request.user.profile.receiving_newsletter
return initial

def get_success_url(self):
messages.add_message(
self.request,
messages.INFO,
"Your profile information has been updated successfully.",
)
return reverse("profile")

def form_valid(self, form):
self.object = form.save()
user = self.object
user.profile.receiving_newsletter = form.cleaned_data["receive_newsletter"]
user.profile.receiving_program_updates = form.cleaned_data[
"receive_program_updates"
]
user.profile.receiving_event_updates = form.cleaned_data[
"receive_event_updates"
]
user.profile.save(
update_fields=[
"receiving_newsletter",
"receiving_program_updates",
"receiving_event_updates",
]
)
"""sends a link for a user to activate their account after changing their email"""
if "email" in form.changed_data:
user.profile.email_confirmed = False
user.profile.save()
send_user_confirmation_email(self.request, user)
return super().form_valid(form)


def unsubscribe(request, user_id, token):
"""
User is immediately unsubscribed if user is found. Otherwise, they are
Expand Down
17 changes: 16 additions & 1 deletion indymeet/templates/registration/profile.html
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ <h2 class="text-3xl">{% translate "Profile Info" %}</h2>
</div>
<div class="row pb-4">
<div class="col">
<table>
<table class="mb-6">
<tbody>
<tr class="border-b-2">
<td class="pt-4 pb-1 pr-6">{% translate "Name" %}</td>
Expand All @@ -35,6 +35,20 @@ <h2 class="text-3xl">{% translate "Profile Info" %}</h2>
<td class="pt-4 pb-1 pr-6">{% translate "Username" %}</td>
<td class="pt-4 pb-1 pr-6">{{ user.username }}</td>
</tr>
<tr class="border-b-2">
<td class="pt-4 pb-1 pr-6">{% translate "Email" %}</td>
<td class="pt-4 pb-1 pr-6">{{ user.email }}</td>
</tr>
<tr class="border-b-2">
<td class="pt-4 pb-1 pr-6">{% translate "Email confirmed" %}</td>
<td class="pt-4 pb-1 pr-6">
{% if user.profile.email_confirmed %}
<i class="fa-solid fa-check"></i> {% translate "Yes" %}
{% else %}
<i class="fa-solid fa-x"></i> {% translate "No" %}
{% endif %}
</td>
</tr>
<tr class="border-b-2">
<td class="pt-4 pb-1 pr-6">{% translate "Receiving Program updates?" %}</td>
<td class="pt-4 pb-1 pr-6">
Expand Down Expand Up @@ -67,6 +81,7 @@ <h2 class="text-3xl">{% translate "Profile Info" %}</h2>
</tr>
</tbody>
</table>
<a class="mt-6 rounded-md bg-primary px-5 py-2.5 text-sm font-medium text-white transition hover:bg-gray-300 hover:text-ds-purple cursor-pointer" href="{% url 'update_user' %}">{% translate "Update" %}</a>
</div>
</div>
</div>
Expand Down
31 changes: 31 additions & 0 deletions indymeet/templates/registration/update_user.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
{% extends "base.html" %}
{% load i18n wagtailcore_tags static %}

{% block title %}{% translate "Update Profile | Djangonaut Space" %}{% endblock %}
{% block meta_title %}{% translate "Update Profile | Djangonaut Space" %}{% endblock %}


{% block extra_css %}
<link rel="stylesheet" href="{% static 'css/registration.css' %}">
{% endblock extra_css %}

{% block content %}
<main class="section my-3 mx-5">
<div class="section-container">
<h1 class="text-5xl mb-6">{% translate "Update Profile Info" %}</h1>

<div class="row ">
<div class="col">
<form method="post">
{% csrf_token %}

{{ form }}
<button type="submit" class="mt-4">{% translate "Save" %}</button>
<a class="px-4 text-ds-purple hover:underline cursor-pointer" href="{% url "profile" %}"> {% trans "Cancel" %}</a>
</form>
</div>
<div class="col"></div>
</div>
</div>
</main>
{% endblock content %}

0 comments on commit 8144884

Please sign in to comment.