feat: add direct-save CMS and About archive
This commit is contained in:
@@ -1,7 +1,21 @@
|
||||
from django.contrib import admin
|
||||
from django.utils import timezone
|
||||
from unfold.admin import ModelAdmin, TabularInline
|
||||
|
||||
from .current import AboutContent, AboutItem, HomeContent, ListingContent, SiteContent
|
||||
from apps.events.models import Event
|
||||
|
||||
from .current import (
|
||||
AboutAudience,
|
||||
AboutContent,
|
||||
AboutGalleryImage,
|
||||
AboutItem,
|
||||
AboutMember,
|
||||
AboutMilestone,
|
||||
AboutYear,
|
||||
HomeContent,
|
||||
ListingContent,
|
||||
SiteContent,
|
||||
)
|
||||
|
||||
|
||||
class FixedContentAdmin(ModelAdmin):
|
||||
@@ -22,6 +36,7 @@ class SiteContentAdmin(FixedContentAdmin):
|
||||
("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")}),
|
||||
)
|
||||
|
||||
@@ -30,10 +45,21 @@ class SiteContentAdmin(FixedContentAdmin):
|
||||
class HomeContentAdmin(FixedContentAdmin):
|
||||
fieldsets = (
|
||||
("Page introduction", {"fields": ("title", "intro")}),
|
||||
("Hero", {"fields": ("hero_heading", "hero_body", "hero_image", "hero_image_alt", "hero_action_label")}),
|
||||
("Featured events", {"fields": ("featured_events_heading", "featured_events_intro", "featured_events_link_label")}),
|
||||
("Featured blog posts", {"fields": ("featured_posts_heading", "featured_posts_intro", "featured_posts_link_label")}),
|
||||
("Participation", {"fields": ("participation_heading", "participation_body", "participation_image", "participation_image_alt", "participation_action_label")}),
|
||||
("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")}),
|
||||
)
|
||||
|
||||
@@ -43,6 +69,100 @@ class AboutItemInline(TabularInline):
|
||||
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")
|
||||
|
||||
@@ -55,19 +175,76 @@ class AboutItemInline(TabularInline):
|
||||
|
||||
@admin.register(AboutContent)
|
||||
class AboutContentAdmin(FixedContentAdmin):
|
||||
inlines = (AboutItemInline,)
|
||||
inlines = (
|
||||
AboutItemInline,
|
||||
AboutAudienceInline,
|
||||
AboutMilestoneInline,
|
||||
LegacyAboutMemberInline,
|
||||
AboutGalleryInline,
|
||||
)
|
||||
fieldsets = (
|
||||
("Page introduction", {"fields": ("title", "intro", "identity_heading", "identity_body")}),
|
||||
("Mission", {"fields": ("mission_heading", "mission_body")}),
|
||||
("Mission, vision, and values", {"fields": (
|
||||
"mission_heading", "mission_body", "vision_heading", "vision_body", "values_heading", "values_body",
|
||||
)}),
|
||||
("Activities", {"fields": ("activities_heading", "activities_body")}),
|
||||
("Values", {"fields": ("values_heading", "values_body")}),
|
||||
("Committees", {"fields": ("committees_heading", "committees_body")}),
|
||||
("Affiliations", {"fields": ("affiliations_heading", "affiliations_body")}),
|
||||
("FAQ and contact", {"fields": ("faq_heading", "contact_heading")}),
|
||||
("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,
|
||||
status__in=(Event.StatusChoices.PUBLISHED, Event.StatusChoices.COMPLETED),
|
||||
start_time__lte=timezone.now(),
|
||||
).order_by("-start_time", "title")
|
||||
return field
|
||||
|
||||
|
||||
@admin.register(ListingContent)
|
||||
class ListingContentAdmin(FixedContentAdmin):
|
||||
list_display = ("slug", "title")
|
||||
|
||||
Reference in New Issue
Block a user