Files
guilan-ace-backend/apps/content/admin.py
Amirhossein Khalili 6c5fadef2f feat: derive event lifecycle status from dates and flags
Replace persisted event statuses with publication and cancellation flags, and compute public lifecycle labels from event timing and registration capacity. Keep filters, registration checks, and profile responses aligned with the derived status.
2026-10-03 12:46:16 +03:30

256 lines
9.8 KiB
Python

from django.contrib import admin
from django.utils import timezone
from unfold.admin import ModelAdmin, TabularInline
from apps.events.models import Event
from .current import (
AboutAudience,
AboutContent,
AboutGalleryImage,
AboutItem,
AboutMember,
AboutMilestone,
AboutYear,
HomeContent,
ListingContent,
SiteContent,
)
class FixedContentAdmin(ModelAdmin):
"""The page records are installed by migration; editors only change them."""
list_per_page = 20
def has_add_permission(self, request):
return False
def has_delete_permission(self, request, obj=None):
return False
@admin.register(SiteContent)
class SiteContentAdmin(FixedContentAdmin):
fieldsets = (
("Association", {"fields": ("official_name", "short_name", "faculty_subline", "description", "logo", "logo_alt")}),
("Navigation labels", {"fields": ("nav_home", "nav_about", "nav_events", "nav_blog")}),
("Contact and footer", {"fields": ("email", "phone", "telegram_url", "instagram_url", "website_url", "address", "footer_copy")}),
("eNAMAD certificate", {"fields": ("enamad_image", "enamad_image_alt", "enamad_url"), "description": "The badge stays unlinked until you add its verification URL."}),
("SEO defaults", {"fields": ("seo_title", "seo_description", "seo_image", "seo_image_alt")}),
)
@admin.register(HomeContent)
class HomeContentAdmin(FixedContentAdmin):
fieldsets = (
("Page introduction", {"fields": ("title", "intro")}),
("Hero", {"fields": (
"hero_heading", "hero_body", "hero_image", "hero_image_alt",
"hero_action_label", "hero_action_url", "hero_secondary_action_label", "hero_secondary_action_url",
), "description": "Destinations accept an internal path such as /events or a secure https URL."}),
("Featured events", {"fields": (
"featured_events_heading", "featured_events_intro", "featured_events_link_label", "featured_events_url", "featured_event_action_label",
), "description": "Destinations accept an internal path such as /events or a secure https URL."}),
("Featured blog posts", {"fields": (
"featured_posts_heading", "featured_posts_intro", "featured_posts_link_label", "featured_posts_url", "featured_post_action_label",
), "description": "Destinations accept an internal path such as /blog or a secure https URL."}),
("Participation", {"fields": (
"participation_heading", "participation_body", "participation_image", "participation_image_alt",
"participation_action_label", "participation_action_url",
"participation_secondary_action_label", "participation_secondary_action_url",
), "description": "Destinations accept an internal path such as /auth or a secure https URL."}),
("SEO", {"fields": ("seo_title", "seo_description", "seo_image", "seo_image_alt")}),
)
class AboutItemInline(TabularInline):
model = AboutItem
extra = 0
fields = ("kind", "title", "text", "position")
def get_queryset(self, request):
return super().get_queryset(request).filter(kind="faq")
def formfield_for_choice_field(self, db_field, request, **kwargs):
field = super().formfield_for_choice_field(db_field, request, **kwargs)
if db_field.name == "kind":
field.choices = tuple(choice for choice in field.choices if choice[0] == "faq")
return field
def has_add_permission(self, request, obj=None):
return request.user.has_perm("content.change_aboutcontent")
def has_change_permission(self, request, obj=None):
return request.user.has_perm("content.change_aboutcontent")
def has_delete_permission(self, request, obj=None):
return request.user.has_perm("content.change_aboutcontent")
class AboutAudienceInline(TabularInline):
model = AboutAudience
extra = 0
fields = ("title", "text", "icon", "position")
ordering = ("position",)
def has_add_permission(self, request, obj=None):
return request.user.has_perm("content.change_aboutcontent")
def has_change_permission(self, request, obj=None):
return request.user.has_perm("content.change_aboutcontent")
def has_delete_permission(self, request, obj=None):
return request.user.has_perm("content.change_aboutcontent")
class AboutMilestoneInline(TabularInline):
model = AboutMilestone
extra = 0
fields = ("year_label", "title", "description", "position")
ordering = ("position",)
def has_add_permission(self, request, obj=None):
return request.user.has_perm("content.change_aboutcontent")
def has_change_permission(self, request, obj=None):
return request.user.has_perm("content.change_aboutcontent")
def has_delete_permission(self, request, obj=None):
return request.user.has_perm("content.change_aboutcontent")
class AboutMemberInline(TabularInline):
model = AboutMember
fk_name = "year_group"
extra = 0
fields = ("name", "role", "bio", "photo", "photo_alt", "position")
ordering = ("position",)
def has_add_permission(self, request, obj=None):
return request.user.has_perm("content.change_aboutcontent")
def has_change_permission(self, request, obj=None):
return request.user.has_perm("content.change_aboutcontent")
def has_delete_permission(self, request, obj=None):
return request.user.has_perm("content.change_aboutcontent")
class LegacyAboutMemberInline(TabularInline):
model = AboutMember
fk_name = "about"
extra = 0
fields = ("name", "role", "entry_year", "bio", "photo", "photo_alt", "position")
ordering = ("entry_year", "position")
def get_queryset(self, request):
return super().get_queryset(request).filter(year_group__isnull=True)
def has_add_permission(self, request, obj=None):
return request.user.has_perm("content.change_aboutcontent")
def has_change_permission(self, request, obj=None):
return request.user.has_perm("content.change_aboutcontent")
def has_delete_permission(self, request, obj=None):
return request.user.has_perm("content.change_aboutcontent")
class AboutGalleryInline(TabularInline):
model = AboutGalleryImage
extra = 0
fields = ("image", "image_alt", "caption", "position")
ordering = ("position",)
def has_add_permission(self, request, obj=None):
return request.user.has_perm("content.change_aboutcontent")
def has_change_permission(self, request, obj=None):
return request.user.has_perm("content.change_aboutcontent")
def has_delete_permission(self, request, obj=None):
return request.user.has_perm("content.change_aboutcontent")
@admin.register(AboutContent)
class AboutContentAdmin(FixedContentAdmin):
inlines = (
AboutItemInline,
AboutAudienceInline,
AboutMilestoneInline,
LegacyAboutMemberInline,
AboutGalleryInline,
)
fieldsets = (
("Page introduction", {"fields": ("title", "intro", "identity_heading", "identity_body")}),
("Mission, vision, and values", {"fields": (
"mission_heading", "mission_body", "vision_heading", "vision_body", "values_heading", "values_body",
)}),
("Activities", {"fields": ("activities_heading", "activities_body")}),
("Association history", {"fields": ("history_heading",)}),
("Who the association serves", {"fields": ("audience_heading",)}),
("Annual archive", {"fields": ("members_heading", "members_intro", "year_events_heading")}),
("Memories gallery", {"fields": ("gallery_heading", "gallery_intro")}),
("FAQ and call to action", {"fields": (
"faq_heading", "cta_heading", "cta_body", "cta_action_label", "cta_action_url",
), "description": "The CTA destination stays empty until you set an internal path or secure https URL."}),
("SEO", {"fields": ("seo_title", "seo_description", "seo_image", "seo_image_alt")}),
)
@admin.register(AboutYear)
class AboutYearAdmin(ModelAdmin):
list_display = ("year_label", "position")
list_editable = ("position",)
ordering = ("position", "pk")
search_fields = ("year_label",)
fieldsets = (
("Academic year", {"fields": ("year_label", "position", "events")}),
)
filter_horizontal = ("events",)
inlines = (AboutMemberInline,)
def has_module_permission(self, request):
return request.user.has_perm("content.change_aboutcontent")
def has_view_permission(self, request, obj=None):
return self.has_module_permission(request)
def has_add_permission(self, request):
return self.has_module_permission(request)
def has_change_permission(self, request, obj=None):
return self.has_module_permission(request)
def has_delete_permission(self, request, obj=None):
return self.has_module_permission(request)
def get_queryset(self, request):
return super().get_queryset(request).filter(about_id=1)
def save_model(self, request, obj, form, change):
if not obj.about_id:
obj.about = AboutContent.objects.get(pk=1)
super().save_model(request, obj, form, change)
def formfield_for_manytomany(self, db_field, request, **kwargs):
field = super().formfield_for_manytomany(db_field, request, **kwargs)
if db_field.name == "events":
field.queryset = Event.objects.filter(
is_deleted=False,
is_published=True,
start_time__lte=timezone.now(),
).order_by("-start_time", "title")
return field
@admin.register(ListingContent)
class ListingContentAdmin(FixedContentAdmin):
list_display = ("slug", "title")
readonly_fields = ("slug",)
fieldsets = (
("Content", {"fields": ("slug", "title", "intro")}),
("SEO", {"fields": ("seo_title", "seo_description", "seo_image", "seo_image_alt")}),
)