diff --git a/portal/forms.py b/portal/forms.py
index 15d9adb..2e945d3 100644
--- a/portal/forms.py
+++ b/portal/forms.py
@@ -1,5 +1,6 @@
from allauth.account.forms import SignupForm
from django import forms
+from django.utils.safestring import mark_safe
class CustomSignupForm(SignupForm):
@@ -10,6 +11,7 @@ class CustomSignupForm(SignupForm):
attrs={'placeholder': 'First Name'}
)
)
+
last_name = forms.CharField(
max_length=200,
label='Last Name',
@@ -17,10 +19,31 @@ class CustomSignupForm(SignupForm):
attrs={'placeholder': 'Last Name'}
)
)
+
+ tos_agreement = forms.BooleanField(
+ required=True,
+ label=mark_safe('I agree to the Terms of Service'),
+ widget=forms.CheckboxInput(attrs={'class': 'form-check-input'})
+ )
+
+ coc_agreement = forms.BooleanField(
+ required=True,
+ label=mark_safe('I agree to the Code of Conduct'),
+ widget=forms.CheckboxInput(attrs={'class': 'form-check-input'})
+ )
+
+ field_order = ['email', 'username', 'first_name', 'last_name', 'password1', 'password2', 'tos_agreement', 'coc_agreement']
def save(self, request):
user = super().save(request)
user.first_name = self.cleaned_data['first_name']
user.last_name = self.cleaned_data['last_name']
user.save()
+
+ from portal_account.models import PortalProfile
+ profile, _ = PortalProfile.objects.get_or_create(user=user)
+ profile.coc_agreement = self.cleaned_data.get('coc_agreement')
+ profile.tos_agreement = self.cleaned_data.get('tos_agreement')
+ profile.save()
+
return user
\ No newline at end of file
diff --git a/portal_account/admin.py b/portal_account/admin.py
index 49aedb3..8655f9f 100644
--- a/portal_account/admin.py
+++ b/portal_account/admin.py
@@ -9,9 +9,11 @@ class PortalProfileAdmin(admin.ModelAdmin):
"user__last_name",
"pronouns",
"coc_agreement",
+ "tos_agreement",
)
search_fields = ("user__email", "user__first_name", "user__last_name")
- list_filter = ("pronouns", "coc_agreement")
+ list_filter = ("pronouns", "coc_agreement", "tos_agreement")
+ readonly_fields = ("coc_agreement", "tos_agreement")
admin.site.register(PortalProfile, PortalProfileAdmin)
diff --git a/portal_account/forms.py b/portal_account/forms.py
index 182d1fc..5892264 100644
--- a/portal_account/forms.py
+++ b/portal_account/forms.py
@@ -12,7 +12,7 @@ class PortalProfileForm(ModelForm):
class Meta:
model = PortalProfile
- fields = ["pronouns", "coc_agreement"]
+ fields = ["pronouns", "coc_agreement", "tos_agreement"]
def clean(self):
cleaned_data = super().clean()
diff --git a/portal_account/migrations/0002_portalprofile_tos_agreement.py b/portal_account/migrations/0002_portalprofile_tos_agreement.py
new file mode 100644
index 0000000..6b5fad9
--- /dev/null
+++ b/portal_account/migrations/0002_portalprofile_tos_agreement.py
@@ -0,0 +1,18 @@
+# Generated by Django 5.1.7 on 2025-04-01 00:43
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('portal_account', '0001_initial'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='portalprofile',
+ name='tos_agreement',
+ field=models.BooleanField(default=False),
+ ),
+ ]
diff --git a/portal_account/models.py b/portal_account/models.py
index 434c34f..5abbc2e 100644
--- a/portal_account/models.py
+++ b/portal_account/models.py
@@ -11,6 +11,7 @@ class PortalProfile(BaseModel):
user = models.OneToOneField(User, on_delete=models.CASCADE)
pronouns = models.CharField(max_length=100, blank=True, null=True)
coc_agreement = models.BooleanField(default=False)
+ tos_agreement = models.BooleanField(default=False)
def get_absolute_url(self):
return reverse("portal_account:portal_profile_edit", kwargs={"pk": self.pk})
diff --git a/portal_account/tests.py b/portal_account/tests.py
index 7ce503c..0970712 100644
--- a/portal_account/tests.py
+++ b/portal_account/tests.py
@@ -1,3 +1,73 @@
from django.test import TestCase
+from django.urls import reverse
+from django.contrib.auth.models import User
+from portal_account.models import PortalProfile
-# Create your tests here.
+class SignupTests(TestCase):
+ def test_signup_requires_tos_agreement(self):
+ """Test that signup requires Terms of Service agreement."""
+ signup_url = reverse('account_signup')
+
+ # Data without tos_agreement
+ data = {
+ 'username': 'testuser',
+ 'email': 'test@example.com',
+ 'password1': 'testpassword123',
+ 'password2': 'testpassword123',
+ 'coc_agreement': True,
+ # tos_agreement is missing
+ }
+
+ response = self.client.post(signup_url, data)
+
+ # Check that the form is invalid
+ self.assertEqual(response.status_code, 200) # Form redisplayed with errors
+ self.assertFalse(User.objects.filter(username='testuser').exists())
+
+ def test_signup_requires_coc_agreement(self):
+ """Test that signup requires Code of Conduct agreement."""
+ signup_url = reverse('account_signup')
+
+ # Data without coc_agreement
+ data = {
+ 'username': 'testuser',
+ 'email': 'test@example.com',
+ 'password1': 'testpassword123',
+ 'password2': 'testpassword123',
+ 'tos_agreement': True,
+ # coc_agreement is missing
+ }
+
+ response = self.client.post(signup_url, data)
+
+ # Check that the form is invalid
+ self.assertEqual(response.status_code, 200) # Form redisplayed with errors
+ self.assertFalse(User.objects.filter(username='testuser').exists())
+
+ def test_successful_signup_with_agreements(self):
+ """Test that signup succeeds when both agreements are checked."""
+ signup_url = reverse('account_signup')
+
+ # Complete data with both agreements
+ data = {
+ 'username': 'testuser',
+ 'email': 'test@example.com',
+ 'password1': 'testpassword123',
+ 'password2': 'testpassword123',
+ "first_name": "Test",
+ "last_name": "User",
+ 'tos_agreement': True,
+ 'coc_agreement': True,
+ }
+
+ response = self.client.post(signup_url, data)
+
+ # Check that the user was created and profile has agreement values
+ self.assertEqual(response.status_code, 302) # Redirect after successful signup
+ self.assertTrue(User.objects.filter(username='testuser').exists())
+
+ # Check that the agreement fields were saved to the profile
+ user = User.objects.get(username='testuser')
+ profile = PortalProfile.objects.get(user=user)
+ self.assertTrue(profile.tos_agreement)
+ self.assertTrue(profile.coc_agreement)