mirror of
https://github.com/pacnpal/thrilltrack-explorer.git
synced 2025-12-20 06:11:11 -05:00
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:
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user