Add email templates for user notifications and account management

- Created a base email template (base.html) for consistent styling across all emails.
- Added moderation approval email template (moderation_approved.html) to notify users of approved submissions.
- Added moderation rejection email template (moderation_rejected.html) to inform users of required changes for their submissions.
- Created password reset email template (password_reset.html) for users requesting to reset their passwords.
- Developed a welcome email template (welcome.html) to greet new users and provide account details and tips for using ThrillWiki.
This commit is contained in:
pacnpal
2025-11-08 15:34:04 -05:00
parent 9c46ef8b03
commit d6ff4cc3a3
335 changed files with 61926 additions and 73 deletions

View File

@@ -2,16 +2,20 @@
Django Admin configuration for media models.
"""
from django.contrib import admin
from django.contrib.contenttypes.admin import GenericTabularInline
from django.utils.html import format_html
from django.utils.safestring import mark_safe
from django.db.models import Count, Q
from .models import Photo
@admin.register(Photo)
class PhotoAdmin(admin.ModelAdmin):
"""Admin interface for Photo model."""
"""Admin interface for Photo model with enhanced features."""
list_display = [
'title', 'cloudflare_image_id', 'photo_type', 'moderation_status',
'is_approved', 'uploaded_by', 'created'
'thumbnail_preview', 'title', 'photo_type', 'moderation_status',
'entity_info', 'uploaded_by', 'dimensions', 'file_size_display', 'created'
]
list_filter = [
'moderation_status', 'is_approved', 'photo_type',
@@ -62,7 +66,79 @@ class PhotoAdmin(admin.ModelAdmin):
}),
)
actions = ['approve_photos', 'reject_photos', 'flag_photos']
date_hierarchy = 'created'
actions = ['approve_photos', 'reject_photos', 'flag_photos', 'make_featured', 'remove_featured']
def get_queryset(self, request):
"""Optimize queryset with select_related."""
qs = super().get_queryset(request)
return qs.select_related(
'uploaded_by', 'moderated_by', 'content_type'
)
def thumbnail_preview(self, obj):
"""Display thumbnail preview in list view."""
if obj.cloudflare_url:
# Use thumbnail variant for preview
from apps.media.services import CloudFlareService
cf = CloudFlareService()
thumbnail_url = cf.get_image_url(obj.cloudflare_image_id, 'thumbnail')
return format_html(
'<img src="{}" style="width: 60px; height: 60px; object-fit: cover; border-radius: 4px;" />',
thumbnail_url
)
return "-"
thumbnail_preview.short_description = "Preview"
def entity_info(self, obj):
"""Display entity information."""
if obj.content_type and obj.object_id:
entity = obj.content_object
if entity:
entity_type = obj.content_type.model
entity_name = getattr(entity, 'name', str(entity))
return format_html(
'<strong>{}</strong><br/><small>{}</small>',
entity_name,
entity_type.upper()
)
return format_html('<em style="color: #999;">Not attached</em>')
entity_info.short_description = "Entity"
def dimensions(self, obj):
"""Display image dimensions."""
if obj.width and obj.height:
return f"{obj.width}×{obj.height}"
return "-"
dimensions.short_description = "Size"
def file_size_display(self, obj):
"""Display file size in human-readable format."""
if obj.file_size:
size_kb = obj.file_size / 1024
if size_kb > 1024:
return f"{size_kb / 1024:.1f} MB"
return f"{size_kb:.1f} KB"
return "-"
file_size_display.short_description = "File Size"
def changelist_view(self, request, extra_context=None):
"""Add statistics to changelist."""
extra_context = extra_context or {}
# Get photo statistics
stats = Photo.objects.aggregate(
total=Count('id'),
pending=Count('id', filter=Q(moderation_status='pending')),
approved=Count('id', filter=Q(moderation_status='approved')),
rejected=Count('id', filter=Q(moderation_status='rejected')),
flagged=Count('id', filter=Q(moderation_status='flagged')),
)
extra_context['photo_stats'] = stats
return super().changelist_view(request, extra_context)
def approve_photos(self, request, queryset):
"""Bulk approve selected photos."""
@@ -90,3 +166,41 @@ class PhotoAdmin(admin.ModelAdmin):
count += 1
self.message_user(request, f"{count} photo(s) flagged for review.")
flag_photos.short_description = "Flag selected photos"
def make_featured(self, request, queryset):
"""Mark selected photos as featured."""
count = queryset.update(is_featured=True)
self.message_user(request, f"{count} photo(s) marked as featured.")
make_featured.short_description = "Mark as featured"
def remove_featured(self, request, queryset):
"""Remove featured status from selected photos."""
count = queryset.update(is_featured=False)
self.message_user(request, f"{count} photo(s) removed from featured.")
remove_featured.short_description = "Remove featured status"
# Inline admin for use in entity admin pages
class PhotoInline(GenericTabularInline):
"""Inline admin for photos in entity pages."""
model = Photo
ct_field = 'content_type'
ct_fk_field = 'object_id'
extra = 0
fields = ['thumbnail_preview', 'title', 'photo_type', 'moderation_status', 'display_order']
readonly_fields = ['thumbnail_preview']
can_delete = True
def thumbnail_preview(self, obj):
"""Display thumbnail preview in inline."""
if obj.cloudflare_url:
from apps.media.services import CloudFlareService
cf = CloudFlareService()
thumbnail_url = cf.get_image_url(obj.cloudflare_image_id, 'thumbnail')
return format_html(
'<img src="{}" style="width: 40px; height: 40px; object-fit: cover; border-radius: 4px;" />',
thumbnail_url
)
return "-"
thumbnail_preview.short_description = "Preview"