Skip to content
Closed
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
26 changes: 26 additions & 0 deletions web/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
WaitingRoom,
)
from .referrals import handle_referral
from .validators import validate_discord_username, validate_github_username, validate_slack_username
from .widgets import (
TailwindCaptchaTextInput,
TailwindCheckboxInput,
Expand Down Expand Up @@ -746,6 +747,31 @@ def clean_username(self):
raise forms.ValidationError("This username is already taken. Please choose a different one.")
return username

def _clean_social_media_username(self, field_name, validator):
"""
Helper method to validate social media usernames.

Reduces code duplication by centralizing the validation logic
for all social media platform usernames.
"""
username = self.cleaned_data.get(field_name, "")
if username:
try:
validator(username)
except ValidationError as e:
# Preserve error codes and full error structure
raise forms.ValidationError(e.message, code=e.code if hasattr(e, 'code') else None)
return username
Comment thread
nourzakhama2003 marked this conversation as resolved.

def clean_discord_username(self):
return self._clean_social_media_username("discord_username", validate_discord_username)

def clean_slack_username(self):
return self._clean_social_media_username("slack_username", validate_slack_username)

def clean_github_username(self):
return self._clean_social_media_username("github_username", validate_github_username)

def save(self, commit=True):
user = super().save(commit=False)
if commit:
Expand Down
65 changes: 65 additions & 0 deletions web/migrations/0064_cleanup_social_media_usernames.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# Generated migration to clean up social media usernames before enforcing new length constraints

from django.db import migrations


def cleanup_social_media_usernames(apps, schema_editor):
"""
Clean up social media usernames that exceed new length limits:
- discord_username: 37 chars (down from 50)
- slack_username: 21 chars (down from 50)
- github_username: 39 chars (down from 50)

This migration truncates over-limit usernames to fit the new constraints.
"""
Profile = apps.get_model("web", "Profile")
updated_count = 0

for profile in Profile.objects.all():
modified = False

# Truncate discord_username to 37 chars
if profile.discord_username and len(profile.discord_username) > 37:
profile.discord_username = profile.discord_username[:37]
modified = True

# Truncate slack_username to 21 chars
if profile.slack_username and len(profile.slack_username) > 21:
profile.slack_username = profile.slack_username[:21]
modified = True

# Truncate github_username to 39 chars
if profile.github_username and len(profile.github_username) > 39:
profile.github_username = profile.github_username[:39]
modified = True

if modified:
# Use update() to bypass validators and Profile.save() with full_clean()
Profile.objects.filter(pk=profile.pk).update(
discord_username=profile.discord_username,
slack_username=profile.slack_username,
github_username=profile.github_username,
)
updated_count += 1

if updated_count > 0:
print(f"✓ Cleaned up {updated_count} profiles with over-limit usernames")


def reverse_cleanup(apps, schema_editor):
"""
Reverse migration - no action needed as truncation is one-way.
Original usernames are lost, so we cannot restore them.
"""
pass


class Migration(migrations.Migration):

dependencies = [
("web", "0063_virtualclassroom_virtualclassroomcustomization_and_more"),
]

operations = [
migrations.RunPython(cleanup_social_media_usernames, reverse_cleanup),
]
44 changes: 44 additions & 0 deletions web/migrations/0065_alter_profile_social_username_fields.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# Schema migration to enforce new field length constraints on social media usernames

from django.db import migrations, models
import web.validators


class Migration(migrations.Migration):

dependencies = [
("web", "0064_cleanup_social_media_usernames"),
]

operations = [
migrations.AlterField(
model_name="profile",
name="discord_username",
field=models.CharField(
blank=True,
help_text="Your Discord username (e.g., User#1234)",
max_length=37,
validators=[web.validators.validate_discord_username],
),
),
migrations.AlterField(
model_name="profile",
name="slack_username",
field=models.CharField(
blank=True,
help_text="Your Slack username",
max_length=21,
validators=[web.validators.validate_slack_username],
),
),
migrations.AlterField(
model_name="profile",
name="github_username",
field=models.CharField(
blank=True,
help_text="Your GitHub username (without @)",
max_length=39,
validators=[web.validators.validate_github_username],
),
),
]
24 changes: 21 additions & 3 deletions web/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
from PIL import Image

from web.utils import calculate_and_update_user_streak
from web.validators import validate_discord_username, validate_github_username, validate_slack_username


class Notification(models.Model):
Expand Down Expand Up @@ -62,9 +63,24 @@ class Profile(models.Model):
)
is_teacher = models.BooleanField(default=False)
is_social_media_manager = models.BooleanField(default=False)
discord_username = models.CharField(max_length=50, blank=True, help_text="Your Discord username (e.g., User#1234)")
slack_username = models.CharField(max_length=50, blank=True, help_text="Your Slack username")
github_username = models.CharField(max_length=50, blank=True, help_text="Your GitHub username (without @)")
discord_username = models.CharField(
max_length=37, # 32 (username) + 1 (#) + 4 (discriminator)
blank=True,
validators=[validate_discord_username],
help_text="Your Discord username (e.g., User#1234)",
)
slack_username = models.CharField(
max_length=21, # Matches Slack's 21 character limit
blank=True,
validators=[validate_slack_username],
help_text="Your Slack username",
)
github_username = models.CharField(
max_length=39, # Matches GitHub's 39 character limit
blank=True,
validators=[validate_github_username],
help_text="Your GitHub username (without @)",
)
Comment thread
nourzakhama2003 marked this conversation as resolved.
referral_code = models.CharField(max_length=20, unique=True, blank=True)
referred_by = models.ForeignKey("self", on_delete=models.SET_NULL, null=True, blank=True, related_name="referrals")
referral_earnings = models.DecimalField(max_digits=10, decimal_places=2, default=0)
Expand Down Expand Up @@ -102,6 +118,8 @@ def __str__(self):
def save(self, *args, **kwargs):
if not self.referral_code:
self.referral_code = self.generate_referral_code()
# Validate social media usernames and other field constraints
self.full_clean()
# Skip image processing for SVG files
super().save(*args, **kwargs)

Expand Down
Loading
Loading