from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from django.utils.html import format_html
from django.contrib.auth.models import Group
from .models import (
User,
UserProfile,
EmailVerification,
PasswordReset,
TopList,
TopListItem,
)
class UserProfileInline(admin.StackedInline):
model = UserProfile
can_delete = False
verbose_name_plural = "Profile"
fieldsets = (
(
"Personal Info",
{"fields": ("display_name", "avatar", "pronouns", "bio")},
),
(
"Social Media",
{"fields": ("twitter", "instagram", "youtube", "discord")},
),
(
"Ride Credits",
{
"fields": (
"coaster_credits",
"dark_ride_credits",
"flat_ride_credits",
"water_ride_credits",
)
},
),
)
class TopListItemInline(admin.TabularInline):
model = TopListItem
extra = 1
fields = ("content_type", "object_id", "rank", "notes")
ordering = ("rank",)
@admin.register(User)
class CustomUserAdmin(UserAdmin):
list_display = (
"username",
"email",
"get_avatar",
"get_status",
"role",
"date_joined",
"last_login",
"get_credits",
)
list_filter = (
"is_active",
"is_staff",
"role",
"is_banned",
"groups",
"date_joined",
)
search_fields = ("username", "email")
ordering = ("-date_joined",)
actions = [
"activate_users",
"deactivate_users",
"ban_users",
"unban_users",
]
inlines = [UserProfileInline]
fieldsets = (
(None, {"fields": ("username", "password")}),
("Personal info", {"fields": ("email", "pending_email")}),
(
"Roles and Permissions",
{
"fields": ("role", "groups", "user_permissions"),
"description": (
"Role determines group membership. Groups determine permissions."
),
},
),
(
"Status",
{
"fields": ("is_active", "is_staff", "is_superuser"),
"description": "These are automatically managed based on role.",
},
),
(
"Ban Status",
{
"fields": ("is_banned", "ban_reason", "ban_date"),
},
),
(
"Preferences",
{
"fields": ("theme_preference",),
},
),
("Important dates", {"fields": ("last_login", "date_joined")}),
)
add_fieldsets = (
(
None,
{
"classes": ("wide",),
"fields": (
"username",
"email",
"password1",
"password2",
"role",
),
},
),
)
@admin.display(description="Avatar")
def get_avatar(self, obj):
if obj.profile.avatar:
return format_html(
'
',
obj.profile.avatar.url,
)
return format_html(
'
{}
',
obj.username[0].upper(),
)
@admin.display(description="Status")
def get_status(self, obj):
if obj.is_banned:
return format_html('Banned')
if not obj.is_active:
return format_html('Inactive')
if obj.is_superuser:
return format_html('Superuser')
if obj.is_staff:
return format_html('Staff')
return format_html('Active')
@admin.display(description="Ride Credits")
def get_credits(self, obj):
try:
profile = obj.profile
return format_html(
"RC: {}
DR: {}
FR: {}
WR: {}",
profile.coaster_credits,
profile.dark_ride_credits,
profile.flat_ride_credits,
profile.water_ride_credits,
)
except UserProfile.DoesNotExist:
return "-"
@admin.action(description="Activate selected users")
def activate_users(self, request, queryset):
queryset.update(is_active=True)
@admin.action(description="Deactivate selected users")
def deactivate_users(self, request, queryset):
queryset.update(is_active=False)
@admin.action(description="Ban selected users")
def ban_users(self, request, queryset):
from django.utils import timezone
queryset.update(is_banned=True, ban_date=timezone.now())
@admin.action(description="Unban selected users")
def unban_users(self, request, queryset):
queryset.update(is_banned=False, ban_date=None, ban_reason="")
def save_model(self, request, obj, form, change):
creating = not obj.pk
super().save_model(request, obj, form, change)
if creating and obj.role != "USER":
# Ensure new user with role gets added to appropriate group
group = Group.objects.filter(name=obj.role).first()
if group:
obj.groups.add(group)
@admin.register(UserProfile)
class UserProfileAdmin(admin.ModelAdmin):
list_display = (
"user",
"display_name",
"coaster_credits",
"dark_ride_credits",
"flat_ride_credits",
"water_ride_credits",
)
list_filter = (
"coaster_credits",
"dark_ride_credits",
"flat_ride_credits",
"water_ride_credits",
)
search_fields = ("user__username", "user__email", "display_name", "bio")
fieldsets = (
(
"User Information",
{"fields": ("user", "display_name", "avatar", "pronouns", "bio")},
),
(
"Social Media",
{"fields": ("twitter", "instagram", "youtube", "discord")},
),
(
"Ride Credits",
{
"fields": (
"coaster_credits",
"dark_ride_credits",
"flat_ride_credits",
"water_ride_credits",
)
},
),
)
@admin.register(EmailVerification)
class EmailVerificationAdmin(admin.ModelAdmin):
list_display = ("user", "created_at", "last_sent", "is_expired")
list_filter = ("created_at", "last_sent")
search_fields = ("user__username", "user__email", "token")
readonly_fields = ("created_at", "last_sent")
fieldsets = (
("Verification Details", {"fields": ("user", "token")}),
("Timing", {"fields": ("created_at", "last_sent")}),
)
@admin.display(description="Status")
def is_expired(self, obj):
from django.utils import timezone
from datetime import timedelta
if timezone.now() - obj.last_sent > timedelta(days=1):
return format_html('Expired')
return format_html('Valid')
@admin.register(TopList)
class TopListAdmin(admin.ModelAdmin):
list_display = ("title", "user", "category", "created_at", "updated_at")
list_filter = ("category", "created_at", "updated_at")
search_fields = ("title", "user__username", "description")
inlines = [TopListItemInline]
fieldsets = (
(
"Basic Information",
{"fields": ("user", "title", "category", "description")},
),
(
"Timestamps",
{"fields": ("created_at", "updated_at"), "classes": ("collapse",)},
),
)
readonly_fields = ("created_at", "updated_at")
@admin.register(TopListItem)
class TopListItemAdmin(admin.ModelAdmin):
list_display = ("top_list", "content_type", "object_id", "rank")
list_filter = ("top_list__category", "rank")
search_fields = ("top_list__title", "notes")
ordering = ("top_list", "rank")
fieldsets = (
("List Information", {"fields": ("top_list", "rank")}),
("Item Details", {"fields": ("content_type", "object_id", "notes")}),
)
@admin.register(PasswordReset)
class PasswordResetAdmin(admin.ModelAdmin):
"""Admin interface for password reset tokens"""
list_display = (
"user",
"created_at",
"expires_at",
"is_expired",
"used",
)
list_filter = (
"used",
"created_at",
"expires_at",
)
search_fields = (
"user__username",
"user__email",
"token",
)
readonly_fields = (
"token",
"created_at",
"expires_at",
)
date_hierarchy = "created_at"
ordering = ("-created_at",)
fieldsets = (
(
"Reset Details",
{
"fields": (
"user",
"token",
"used",
)
},
),
(
"Timing",
{
"fields": (
"created_at",
"expires_at",
)
},
),
)
@admin.display(description="Status", boolean=True)
def is_expired(self, obj):
"""Display expiration status with color coding"""
from django.utils import timezone
if obj.used:
return format_html('Used')
elif timezone.now() > obj.expires_at:
return format_html('Expired')
return format_html('Valid')
def has_add_permission(self, request):
"""Disable manual creation of password reset tokens"""
return False
def has_change_permission(self, request, obj=None):
"""Allow viewing but restrict editing of password reset tokens"""
return getattr(request.user, "is_superuser", False)