Skip to content

Commit

Permalink
updates
Browse files Browse the repository at this point in the history
  • Loading branch information
JasonLovesDoggo committed Jan 4, 2024
1 parent ca99a5b commit 1b0a3fb
Show file tree
Hide file tree
Showing 8 changed files with 117 additions and 112 deletions.
16 changes: 13 additions & 3 deletions core/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,16 @@
from django_select2 import forms as s2forms
from martor.widgets import AdminMartorWidget

from . import models
from core import models
from django.contrib.auth.forms import (
UserChangeForm as ContribUserChangeForm,
UserCreationForm as ContribUserCreationForm,
)

from core.views.mixins import CaseInsensitiveUsernameMixin

class MetropolisSignupForm(SignupForm):

class MetropolisSignupForm(SignupForm, CaseInsensitiveUsernameMixin):
first_name = forms.CharField(
max_length=30,
label="First Name",
Expand Down Expand Up @@ -282,5 +288,9 @@ def __init__(self, *args, **kwargs):
self.fields["status"].initial = "d"


class UserAdminForm(forms.ModelForm):
class UserAdminForm(CaseInsensitiveUsernameMixin, ContribUserChangeForm):
expo_notif_tokens = forms.JSONField(required=False)


class UserCreationForm(CaseInsensitiveUsernameMixin, ContribUserCreationForm):
pass
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Generated by Django 5.0 on 2024-01-04 12:44

import core.models.user
from django.db import migrations


class Migration(migrations.Migration):
dependencies = [
("core", "0070_remove_staffmember_unique_staff_member_and_more"),
]

operations = [
migrations.AlterModelOptions(
name="user",
options={"verbose_name": "user", "verbose_name_plural": "users"},
),
migrations.AlterModelManagers(
name="user",
managers=[
("objects", core.models.user.CaseInsensitiveUserManager()),
],
),
migrations.RemoveConstraint(
model_name="user",
name="username-lower-check",
),
]
18 changes: 11 additions & 7 deletions core/models/user.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
from django.conf import settings
from django.contrib.auth.models import AbstractUser
from django.contrib.auth.models import AbstractUser, UserManager
from django.core.exceptions import ObjectDoesNotExist
from django.db import models
from django.db.models import Q, CharField
from django.db.models.functions import Lower
from django.utils import timezone

from .choices import graduating_year_choices, timezone_choices
Expand All @@ -16,11 +15,21 @@
# Create your models here.


class CaseInsensitiveUserManager(UserManager):
def get_by_natural_key(self, username):
"""
By default, Django does a case-sensitive check on usernames. This is Wrong™.
Overriding this method fixes it.
"""
return self.get(**{self.model.USERNAME_FIELD + "__iexact": username})


def get_default_user_timezone():
return settings.DEFAULT_TIMEZONE


class User(AbstractUser):
objects = CaseInsensitiveUserManager()
bio = models.TextField(blank=True)
timezone = models.CharField(
max_length=50, choices=timezone_choices, default=get_default_user_timezone
Expand Down Expand Up @@ -97,11 +106,6 @@ def can_approve(self, obj):
def all(cls):
return cls.objects.filter(is_active=True)

class Meta:
constraints = [
models.UniqueConstraint(Lower("username"), name="username-lower-check")
]


class StaffMember(models.Model):
user = models.OneToOneField(
Expand Down
80 changes: 40 additions & 40 deletions core/urls.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,23 @@
from django.contrib.sitemaps.views import sitemap
from django.urls import include, path

from . import views
from .api.views import MartorImageUpload
from .api.views import *
from .utils.sitemaps import *
from .views.index import *
from .views.organization import OrganizationShort
from .views.post import *
from .views.raffle import *
from .views.timetable import *
from .views.tv import *
from .views.user import *

urlpatterns = [
path(
"martor/api/upload-image/",
MartorImageUpload.as_view(),
name="api_martor_image_upload",
),
path("", views.Index.as_view(), name="index"),
path("", Index.as_view(), name="index"),
path(
"sitemap.xml",
sitemap,
Expand All @@ -22,76 +28,70 @@
"clubs": ClubsSitemap,
}
},
name="django.contrib.sitemaps.views.sitemaps",
name="django.contrib.sitemaps.sitemaps",
),
path("api/", include("core.api.urls")),
path("timetable", views.TimetableList.as_view(), name="timetable_list"),
path("timetable", TimetableList.as_view(), name="timetable_list"),
path(
"timetable/add/term/<int:pk>",
views.TimetableCreate.as_view(),
TimetableCreate.as_view(),
name="timetable_create",
),
path(
"timetable/edit/<int:pk>",
views.TimetableUpdate.as_view(),
TimetableUpdate.as_view(),
name="timetable_update",
),
path(
"course/add/term/<int:pk>", views.CourseCreate.as_view(), name="course_create"
),
path("accounts/profile", views.ProfileRedirect.as_view(), name="profile_redirect"),
path(
"accounts/profile/update", views.ProfileUpdate.as_view(), name="profile_update"
),
path("user/<str:slug>", views.Profile.as_view(), name="profile_detail"),
path("clubs", views.OrganizationList.as_view(), name="organization_list"),
path("course/add/term/<int:pk>", CourseCreate.as_view(), name="course_create"),
path("accounts/profile", ProfileRedirect.as_view(), name="profile_redirect"),
path("accounts/profile/update", ProfileUpdate.as_view(), name="profile_update"),
path("user/<str:slug>", Profile.as_view(), name="profile_detail"),
path("clubs", OrganizationList.as_view(), name="organization_list"),
path(
"club/<str:slug>",
views.OrganizationDetail.as_view(),
OrganizationDetail.as_view(),
name="organization_detail",
),
path("announcements", views.AnnouncementList.as_view(), name="announcement_list"),
path("announcements", AnnouncementList.as_view(), name="announcement_list"),
path(
"announcement/<int:pk>",
views.AnnouncementDetail.as_view(),
AnnouncementDetail.as_view(),
name="announcement_detail",
),
path(
"announcements/tag/<int:tag>",
views.AnnouncementTagList.as_view(),
AnnouncementTagList.as_view(),
name="announcement_tag_list",
),
path("announcements/feed", views.AnnouncementFeed(), name="announcement_feed"),
path("gallery", views.ExhibitList.as_view(), name="exhibit_list"),
path("blog", views.BlogPostList.as_view(), name="blogpost_list"),
path("blog/<str:slug>", views.BlogPostDetail.as_view(), name="blogpost_detail"),
path(
"blog/tag/<int:tag>", views.BlogPostTagList.as_view(), name="blogpost_tag_list"
),
path("calendar", views.CalendarView.as_view(), name="calendar"),
path("calendar.ics", views.CalendarFeed(), name="calendar_ical"),
path("map", views.MapView.as_view(), name="map"),
path("about", views.AboutView.as_view(), name="about"),
path("teapot", views.Teapot.as_view(), name="teapot"),
path("justinian", views.Justinian.as_view(), name="justinian"),
path("json", views.Json.as_view(), name="json"),
path("tv", views.TVView.as_view(), name="tv"),
path("tv/clubs", views.TVClubView.as_view(), name="tvclub"),
path("c/<int:pk>", views.OrganizationShort.as_view(), name="organization_short"),
path("raffle", views.RaffleRedirect.as_view(), name="raffle"),
path("announcements/feed", AnnouncementFeed(), name="announcement_feed"),
path("gallery", ExhibitList.as_view(), name="exhibit_list"),
path("blog", BlogPostList.as_view(), name="blogpost_list"),
path("blog/<str:slug>", BlogPostDetail.as_view(), name="blogpost_detail"),
path("blog/tag/<int:tag>", BlogPostTagList.as_view(), name="blogpost_tag_list"),
path("calendar", CalendarView.as_view(), name="calendar"),
path("calendar.ics", CalendarFeed(), name="calendar_ical"),
path("map", MapView.as_view(), name="map"),
path("about", AboutView.as_view(), name="about"),
path("teapot", Teapot.as_view(), name="teapot"),
path("justinian", Justinian.as_view(), name="justinian"),
path("json", Json.as_view(), name="json"),
path("tv", TVView.as_view(), name="tv"),
path("tv/clubs", TVClubView.as_view(), name="tvclub"),
path("c/<int:pk>", OrganizationShort.as_view(), name="organization_short"),
path("raffle", RaffleRedirect.as_view(), name="raffle"),
path("hijack/", include("hijack.urls")),
]

if settings.LAZY_LOADING:
urlpatterns += [
path(
"announcements/cards",
views.AnnouncementCards.as_view(),
AnnouncementCards.as_view(),
name="api_announcements_card",
),
path(
"blogs/cards",
views.BlogPostCards.as_view(),
BlogPostCards.as_view(),
name="api_blog_card",
),
]
52 changes: 0 additions & 52 deletions core/utils/actions.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,58 +119,6 @@ def send_notif_singleday(modeladmin, request, queryset):
notif_events_singleday.delay(date=dt.date.today())


class AdminPasswordResetForm(ActionForm):
new_password = forms.CharField(
required=False,
label=_(" New password "),
help_text="The password to set for the user if you are using the reset password action",
)


@admin_action_rate_limit(
rate_limit=1, time_period=60 * 60 * 6, scope="user"
) # specific SU can only reset one password every 12 hours, to prevent abuse. if more is needed contact the backend team to reset.
@admin.action(
permissions=["change"], description=_("Reset the password for the selected user")
)
def reset_password(modeladmin, request, queryset):
user = queryset.first()
if not request.user.is_superuser:
modeladmin.message_user(
request,
"You must be a superuser to reset passwords.",
level=messages.WARNING,
)
return
if user.is_superuser:
modeladmin.message_user(
request,
"You cannot reset the password of a superuser, please contact the backend lead to reset the password.",
level=messages.ERROR,
)
return
if len(queryset) > 1:
modeladmin.message_user(
request, "Please only select one user at a time.", level=messages.ERROR
)
return
if not request.POST["new_password"]:
modeladmin.message_user(
request,
"Please enter a new password in the 'New Password' field.",
level=messages.ERROR,
)
return
user.set_password(request.POST["new_password"])
user.save()
modeladmin.message_user(
request, f"Password for {user} has been set to the specified password."
)


# FlatPages


@admin.action(
permissions=["change"],
description="Archive selected flatpages and download them as a JSON file",
Expand Down
7 changes: 0 additions & 7 deletions core/views/__init__.py

This file was deleted.

23 changes: 23 additions & 0 deletions core/views/mixins.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,27 @@
from django.views.generic.base import ContextMixin
from django import forms
from django.contrib.auth import get_user_model
from django.utils.translation import gettext_lazy as _


class CaseInsensitiveUsernameMixin:
"""
Disallow a username with a case-insensitive match of existing usernames.
Add this mixin to any forms that use the User object
"""

def clean_username(self):
username = self.cleaned_data.get("username")
if (
get_user_model()
.objects.filter(username__iexact=username)
.exclude(pk=self.instance.pk)
.exists()
):
raise forms.ValidationError(
_("The username ‘{}’ is already in use.".format(username))
)
return username


class TitleMixin(ContextMixin):
Expand Down
6 changes: 3 additions & 3 deletions core/views/timetable.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@
from django.views.generic.edit import CreateView, FormMixin, UpdateView

from . import mixins
from .. import models
from ..forms import (
AddCourseForm,
from core import models
from core.forms import (
AddTimetableSelectTermForm,
TimetableSelectCoursesForm,
AddCourseForm,
)


Expand Down

0 comments on commit 1b0a3fb

Please sign in to comment.