from django.db import models from django.db.models import SET_NULL from modelcluster.fields import ParentalKey from modelcluster.models import ClusterableModel from wagtail.admin.panels import FieldPanel, InlinePanel, MultiFieldPanel from wagtail.contrib.settings.models import BaseSiteSetting, register_setting from wagtail.models import Orderable SOCIAL_ICON_CHOICES = [ ("twitter", "Twitter / X"), ("github", "GitHub"), ("rss", "RSS Feed"), ("linkedin", "LinkedIn"), ("youtube", "YouTube"), ("mastodon", "Mastodon"), ("bluesky", "Bluesky"), ] @register_setting class SiteSettings(ClusterableModel, BaseSiteSetting): default_og_image = models.ForeignKey( "wagtailimages.Image", null=True, blank=True, on_delete=SET_NULL, related_name="+", ) privacy_policy_page = models.ForeignKey( "wagtailcore.Page", null=True, blank=True, on_delete=SET_NULL, related_name="+", ) # Branding site_name = models.CharField(max_length=100, default="NO HYPE AI") tagline = models.CharField( max_length=200, default="Honest AI tool reviews for developers.", ) footer_description = models.TextField( default="In-depth reviews and benchmarks of the latest AI coding tools.\nHonest analysis for developers.", blank=True, ) copyright_text = models.CharField( max_length=200, default="No Hype AI. All rights reserved.", ) panels = [ MultiFieldPanel( [ FieldPanel("site_name"), FieldPanel("tagline"), FieldPanel("footer_description"), FieldPanel("copyright_text"), ], heading="Branding", ), MultiFieldPanel( [ FieldPanel("default_og_image"), FieldPanel("privacy_policy_page"), ], heading="SEO & Legal", ), InlinePanel("navigation_items", label="Navigation Menu Items"), InlinePanel("social_links", label="Social Media Links"), ] class NavigationMenuItem(Orderable): settings = ParentalKey( SiteSettings, on_delete=models.CASCADE, related_name="navigation_items", ) link_page = models.ForeignKey( "wagtailcore.Page", null=True, blank=True, on_delete=SET_NULL, related_name="+", help_text="Link to an internal page. If unpublished, the link is hidden automatically.", ) link_url = models.CharField( max_length=500, blank=True, default="", help_text="URL or path (used only when no page is selected).", ) link_title = models.CharField( max_length=100, blank=True, default="", help_text="Override the display text. If blank, the page title is used.", ) open_in_new_tab = models.BooleanField(default=False) show_in_header = models.BooleanField(default=True) show_in_footer = models.BooleanField(default=True) panels = [ FieldPanel("link_page"), FieldPanel("link_url"), FieldPanel("link_title"), FieldPanel("open_in_new_tab"), FieldPanel("show_in_header"), FieldPanel("show_in_footer"), ] @property def title(self): if self.link_title: return self.link_title if self.link_page: return self.link_page.title return "" @property def url(self): if self.link_page: return self.link_page.url return self.link_url @property def is_live(self): """Return False if linked to an unpublished/non-live page.""" if self.link_page_id: return self.link_page.live return bool(self.link_url) class Meta(Orderable.Meta): pass class SocialMediaLink(Orderable): settings = ParentalKey( SiteSettings, on_delete=models.CASCADE, related_name="social_links", ) platform = models.CharField( max_length=30, choices=SOCIAL_ICON_CHOICES, ) url = models.CharField(max_length=500, help_text="URL or path (e.g. https://twitter.com/… or /feed/).") label = models.CharField( max_length=100, blank=True, default="", help_text="Display label. If blank, the platform name is used.", ) panels = [ FieldPanel("platform"), FieldPanel("url"), FieldPanel("label"), ] @property def display_label(self): if self.label: return self.label return dict(SOCIAL_ICON_CHOICES).get(self.platform, self.platform) @property def icon_template(self): return f"components/icons/{self.platform}.html" class Meta(Orderable.Meta): pass