# SPDX-FileCopyrightText: 2024 Thomas Breitner <t.breitner@csl.mpg.de>
#
# SPDX-License-Identifier: EUPL-1.2
import pathlib
from dataclasses import dataclass
from typing import Optional
from django.db import models
from django.utils.html import strip_tags
from django.utils.text import Truncator
from django.db.models.fields.files import ImageFieldFile
from wagtail.models import Page
from wagtail.admin.panels import (
FieldPanel,
MultiFieldPanel,
PageChooserPanel,
)
from wagtail.images.models import Image
# Links
[docs]
class LinkFields(models.Model):
link_external = models.URLField("External link", blank=True)
link_page = models.ForeignKey(
"wagtailcore.Page",
null=True,
blank=True,
related_name="+",
on_delete=models.SET_NULL,
)
link_document = models.ForeignKey(
"wagtaildocs.Document",
null=True,
blank=True,
related_name="+",
on_delete=models.SET_NULL,
)
@property
def link_target(self):
if self.link_external.endswith("feed/"):
return "feed"
elif self.link_page:
return "internal_link"
elif self.link_document:
extension = self.link_document.file_extension
if extension == "pdf":
return "document_pdf"
elif extension.startswith("doc") or extension.startswith("dot"):
return "document_word"
else:
return "internal_link"
else:
return "external_link"
@property
def link(self):
if self.link_page:
return self.link_page.url
elif self.link_document:
return self.link_document.url
else:
return self.link_external
panels = [
FieldPanel("link_external"),
PageChooserPanel("link_page"),
FieldPanel("link_document"),
]
# Related links
# Carousel items
[docs]
class CarouselItem(LinkFields):
image = models.ForeignKey(
"wagtailimages.Image",
null=True,
blank=True,
on_delete=models.SET_NULL,
related_name="+",
)
embed_url = models.URLField("Embed URL", blank=True)
caption = models.CharField(max_length=255, blank=True)
panels = [
FieldPanel("image"),
FieldPanel("embed_url"),
FieldPanel("caption"),
MultiFieldPanel(LinkFields.panels, "Link"),
]
# Categories
[docs]
class AbstractNewsCategory(models.Model):
title = models.CharField(
max_length=200,
help_text="Short descriptive title for this category.",
)
description = models.CharField(
max_length=255,
blank=True,
help_text="Additional descriptive text",
)
slug = models.SlugField(
max_length=255,
unique=True,
help_text="Short descriptive unique name for use in urls.",
)
print_order = models.PositiveSmallIntegerField(
default=999,
help_text="Positive integer by which the news exported for the printed issue will be ordered. Small integer means ordered first.",
)
panels = [
FieldPanel("title"),
FieldPanel("slug"),
FieldPanel("description"),
FieldPanel("print_order"),
]
search_fields = Page.search_fields + [
"title__icontains",
"description__icontains",
"slug__icontains",
]
def __str__(self):
return self.title
[docs]
class BasePage(Page):
@property
def get_og_title(self):
og_title = self.get_site().site_name
if hasattr(self, "get_obj_og_title") and self.get_obj_og_title:
og_title = self.get_obj_og_title
elif self.seo_title:
og_title = self.seo_title
elif self.title:
og_title = self.title
return og_title
@property
def get_og_description(self):
og_description = self.title
if self.search_description:
og_description = self.search_description
elif hasattr(self, "get_obj_og_description") and self.get_obj_og_description:
og_description = strip_tags(self.get_obj_og_description)
og_description = Truncator(og_description).words(40)
return og_description
@property
def og_image(self):
og_image = {
"image": None,
"image_extension": None,
"image_class": None,
}
_image = _image_extension = _image_class = None
if hasattr(self, "get_obj_og_image") and self.get_obj_og_image:
obj_image = self.get_obj_og_image
if isinstance(obj_image, Image):
_image = obj_image
_image_extension = pathlib.Path(obj_image.filename).suffix[1:]
_image_class = "wagtail_Image"
elif isinstance(obj_image, ImageFieldFile):
_image = obj_image.url
_image_extension = pathlib.Path(obj_image.url).suffix[1:]
_image_class = "django_ImageFieldFile"
og_image = {
"image": _image,
"image_extension": _image_extension,
"image_class": _image_class,
}
# print(f"{og_image=}")
return og_image
[docs]
class Meta:
abstract = True
[docs]
class AbstractPublicationPage(BasePage):
"""
Base Page class.
Currently used for ArticlePage and NewsPage.
"""
@property
def get_pdf_page_reference(self) -> Optional[PdfPageReference]:
"""
Return a PdfPageReference with human-readable page numbering text and
the corresponding anchor page in the PDF, or None if not applicable.
"""
if not self.issue:
return None
print_cover_pages = 3
print_start_page = self.issue.start_page_numbering_at or print_cover_pages
subtrahend = print_start_page - print_cover_pages
anchor_page = (
(self.issue_page_from - subtrahend) if self.issue_page_from else None
)
page_abbr = "pp" if self.issue_page_to else "p"
page_from = self.issue_page_from or ""
page_to = self.issue_page_to or ""
page_delimiter = " – " if self.issue_page_to else ""
text = ""
if page_from:
text = f"{page_abbr} {page_from}{page_delimiter}{page_to}"
return PdfPageReference(text=text, anchor=anchor_page)
[docs]
class Meta:
abstract = True