Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions portal/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@
"django.contrib.messages.middleware.MessageMiddleware",
"django.middleware.clickjacking.XFrameOptionsMiddleware",
"allauth.account.middleware.AccountMiddleware",
"portal_account.middleware.TOSRedirectMiddleware",
]

ROOT_URLCONF = "portal.urls"
Expand Down
15 changes: 3 additions & 12 deletions portal/views.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,6 @@
from django.shortcuts import redirect, render
from django.shortcuts import render


def index(request):
"""Redirect to profile creation page if user has not completed their profile."""
context = {}
from portal_account.models import PortalProfile

if (
request.user
and request.user.is_authenticated
and not PortalProfile.objects.filter(user=request.user).exists()
):
return redirect("portal_account:portal_profile_new")
return render(request, "portal/index.html", context)
"""Display the index page for all users."""
return render(request, "portal/index.html")
9 changes: 7 additions & 2 deletions portal_account/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ class PortalProfileForm(ModelForm):
first_name = forms.CharField()
last_name = forms.CharField()
email = forms.CharField(disabled=True)
coc_agreement = forms.BooleanField(disabled=True, required=False)
tos_agreement = forms.BooleanField(disabled=True, required=False)
coc_agreement = forms.BooleanField(required=False)
tos_agreement = forms.BooleanField(required=False)

class Meta:
model = PortalProfile
Expand All @@ -32,6 +32,11 @@ def __init__(self, *args, **kwargs):
self.fields["first_name"].initial = self.user.first_name
self.fields["last_name"].initial = self.user.last_name

if self.instance.pk:
if self.instance.coc_agreement and self.instance.tos_agreement:
self.fields["coc_agreement"].disabled = True
self.fields["tos_agreement"].disabled = True

# fix field order
self.order_fields(
[
Expand Down
37 changes: 37 additions & 0 deletions portal_account/middleware.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
from django.shortcuts import redirect


class TOSRedirectMiddleware:
def __init__(self, get_response):
self.get_response = get_response

def __call__(self, request):
if not request.user.is_authenticated:
return self.get_response(request)

excluded_path_prefixes = [
"/admin/",
"/accounts/",
"/portal_account/profile/new",
"/portal_account/profile/edit/",
"/portal_account/profile/view/",
"/volunteer/",
]

current_path = request.path
if any(current_path.startswith(prefix) for prefix in excluded_path_prefixes):
return self.get_response(request)

try:
from portal_account.models import PortalProfile

profile = PortalProfile.objects.get(user=request.user)

if not profile.tos_agreement:
profile_edit_url = profile.get_absolute_url()
if current_path != profile_edit_url:
return redirect(profile_edit_url)
except PortalProfile.DoesNotExist:
return redirect("portal_account:portal_profile_new")

return self.get_response(request)
17 changes: 15 additions & 2 deletions templates/portal_account/portalprofile_edit.html
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,27 @@ <h1 class="display-5">
{% bootstrap_form_errors form %}
<form action="{% url 'portal_account:portal_profile_edit' object.pk %}"
method="post"
class="form">
class="form"
enctype="multipart/form-data">
{% csrf_token %}
{% bootstrap_field form.username %}
{% bootstrap_field form.first_name %}
{% bootstrap_field form.last_name %}
{% bootstrap_field form.email %}
{% bootstrap_field form.pronouns %}
{% bootstrap_field form.coc_agreement %}
{% if form.coc_agreement.value and form.tos_agreement.value %}
<div class="alert alert-success">
You've already agreed to the Code of Conduct and Terms of Service.
</div>
{% bootstrap_field form.coc_agreement %}
{% bootstrap_field form.tos_agreement %}
{% else %}
<div class="alert alert-warning">
<strong>Important:</strong> You must agree to both the Code of Conduct and Terms of Service to complete your profile.
</div>
{% bootstrap_field form.coc_agreement %}
{% bootstrap_field form.tos_agreement %}
{% endif %}
<div class="mt-4">
<input type="submit" value="Update Profile" class="btn btn-primary" />
<a href="{% url 'portal_account:portal_profile_detail' object.pk %}"
Expand Down
17 changes: 15 additions & 2 deletions templates/portal_account/portalprofile_form.html
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,27 @@ <h1 class="display-5">

<form action="{% if object.pk %}{% url 'portal_account:portal_profile_edit' object.pk %}{% else %}{% url 'portal_account:portal_profile_new' %}{% endif %}"
method="post"
class="form">
class="form"
enctype="multipart/form-data">
{% csrf_token %}
{% bootstrap_field form.username %}
{% bootstrap_field form.first_name %}
{% bootstrap_field form.last_name %}
{% bootstrap_field form.email %}
{% bootstrap_field form.pronouns %}
{% bootstrap_field form.coc_agreement %}
{% if form.coc_agreement.value and form.tos_agreement.value %}
<div class="alert alert-success">
You've already agreed to the Code of Conduct and Terms of Service.
</div>
{% bootstrap_field form.coc_agreement %}
{% bootstrap_field form.tos_agreement %}
{% else %}
<div class="alert alert-warning">
<strong>Important:</strong> You must agree to both the Code of Conduct and Terms of Service to complete your profile.
</div>
{% bootstrap_field form.coc_agreement %}
{% bootstrap_field form.tos_agreement %}
{% endif %}

<div class="mt-4">
<input type="submit"
Expand Down
36 changes: 8 additions & 28 deletions tests/portal/test_views.py
Original file line number Diff line number Diff line change
@@ -1,36 +1,16 @@
import pytest
from django.urls import reverse
from pytest_django.asserts import assertRedirects

from portal_account.models import PortalProfile
from pytest_django.asserts import assertTemplateUsed


@pytest.mark.django_db
class TestPortalIndex:

def test_index_unauthenticated(self, client):

class TestIndexView:
def test_access(self, client):
"""
Test users can access the index page.
Should return 200 status code and render the correct template.
"""
response = client.get(reverse("index"))
assert response.status_code == 200
assert "Sign up" in response.content.decode()
assert "Login" in response.content.decode()

def test_index_authenticated_no_profile_created(self, client, portal_user):

client.force_login(portal_user)
response = client.get(reverse("index"), follow=True)

assert "Sign out" not in response.content.decode()
assert "Login" not in response.content.decode()
assertRedirects(response, reverse("portal_account:portal_profile_new"))

def test_index_authenticated_profile_already_created(self, client, portal_user):

portal_profile = PortalProfile(user=portal_user)
portal_profile.save()

client.force_login(portal_user)
response = client.get(reverse("index"))
assert response.status_code == 200
assert "Sign out" not in response.content.decode()
assert "Login" not in response.content.decode()
assertTemplateUsed(response, "portal/index.html")
50 changes: 50 additions & 0 deletions tests/portal_account/test_forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,56 @@ def test_profile_form_required_fields(self, portal_user):
assert not form.is_valid()


@pytest.mark.django_db
class TestPortalProfileFormAgreements:
def test_agreements_disabled_when_both_true(self, portal_user):
portal_profile, _ = PortalProfile.objects.get_or_create(
user=portal_user, defaults={"coc_agreement": True, "tos_agreement": True}
)
portal_profile.coc_agreement = True
portal_profile.tos_agreement = True
portal_profile.save()

form = PortalProfileForm(user=portal_user, instance=portal_profile)

assert form.fields["coc_agreement"].disabled is True
assert form.fields["tos_agreement"].disabled is True

def test_agreements_not_disabled_when_not_both_true(self, portal_user):
portal_profile, _ = PortalProfile.objects.get_or_create(user=portal_user)

portal_profile.coc_agreement = True
portal_profile.tos_agreement = False
portal_profile.save()

form = PortalProfileForm(user=portal_user, instance=portal_profile)
assert form.fields["coc_agreement"].disabled is False
assert form.fields["tos_agreement"].disabled is False

portal_profile.coc_agreement = False
portal_profile.tos_agreement = True
portal_profile.save()

form = PortalProfileForm(user=portal_user, instance=portal_profile)
assert form.fields["coc_agreement"].disabled is False
assert form.fields["tos_agreement"].disabled is False

portal_profile.coc_agreement = False
portal_profile.tos_agreement = False
portal_profile.save()

form = PortalProfileForm(user=portal_user, instance=portal_profile)
assert form.fields["coc_agreement"].disabled is False
assert form.fields["tos_agreement"].disabled is False

def test_agreements_not_disabled_for_new_profile(self, portal_user):
PortalProfile.objects.filter(user=portal_user).delete()

form = PortalProfileForm(user=portal_user)
assert form.fields["coc_agreement"].disabled is False
assert form.fields["tos_agreement"].disabled is False


@pytest.mark.django_db
class TestSignupView:
def test_error_styling_on_invalid_signup(self, client):
Expand Down
Loading