From 877382559098fab282977383711725841f5df6df Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Valenta?= <git@imaniti.org>
Date: Mon, 21 Aug 2023 07:01:25 +0200
Subject: [PATCH] add footnotes

---
 home/models.py                              | 17 +++++++++
 home/templates/home/home_article_page.html  |  6 ++-
 home/templates/home/includes/footnotes.html | 20 ++++++++++
 home/templatetags/footnotes.py              | 42 +++++++++++++++++++++
 institut/settings/base.py                   |  1 +
 institut/urls.py                            |  2 +
 requirements/base.txt                       |  5 ++-
 7 files changed, 89 insertions(+), 4 deletions(-)
 create mode 100644 home/templates/home/includes/footnotes.html
 create mode 100644 home/templatetags/footnotes.py

diff --git a/home/models.py b/home/models.py
index 80c57b4..6b4e563 100644
--- a/home/models.py
+++ b/home/models.py
@@ -6,6 +6,7 @@ from taggit.models import TaggedItemBase
 from wagtail.admin.panels import (
     FieldPanel,
     MultiFieldPanel,
+    InlinePanel,
     ObjectList,
     TabbedInterface,
 )
@@ -403,6 +404,21 @@ class HomeArticlePage(HomeContentPageMixin):
         verbose_name="Obrázek",
     )
 
+    content = RichTextField(
+        verbose_name="Obsah",
+        features=[
+            "h2", "h3", "h4",
+            "bold", "italic",
+            "ol", "ul",
+            "hr",
+            "link",
+            "document-link",
+            "image",
+            "embed",
+            "footnotes",
+        ],
+    )
+
     show_image_on_homepage = models.BooleanField(
         verbose_name="Zobrazovat obrázek na homepage",
         default=False,
@@ -431,6 +447,7 @@ class HomeArticlePage(HomeContentPageMixin):
         FieldPanel("perex", icon="pilcrow"),
         FieldPanel("tags", icon="tag"),
         FieldPanel("content", icon="pilcrow"),
+        InlinePanel("footnotes", label="Footnotes", icon="pilcrow"),
     ]
 
     class Meta:
diff --git a/home/templates/home/home_article_page.html b/home/templates/home/home_article_page.html
index 64cf5ef..b3f3ccf 100644
--- a/home/templates/home/home_article_page.html
+++ b/home/templates/home/home_article_page.html
@@ -1,5 +1,5 @@
 {% extends "base.html" %}
-{% load static wagtailcore_tags wagtailimages_tags %}
+{% load static wagtailcore_tags wagtailimages_tags footnotes %}
 
 {% block content %}
 <main class="flex flex-col items-center gap-10 pt-14">
@@ -42,7 +42,9 @@
 
             <p class="mb-3">{{ page.perex }}</p>
 
-            {{ page.content|richtext }}
+            {% richtext_footnotes page.content|richtext %}
+
+            {% include "home/includes/footnotes.html" %}
         </div>
     </div>
 </main>
diff --git a/home/templates/home/includes/footnotes.html b/home/templates/home/includes/footnotes.html
new file mode 100644
index 0000000..39cfae5
--- /dev/null
+++ b/home/templates/home/includes/footnotes.html
@@ -0,0 +1,20 @@
+{% load wagtailcore_tags %}
+
+{% if page.footnotes_list %}
+    <div class="footnotes" id="footnotes">
+        <hr class="my-8">
+        <ol>
+            {% for footnote in page.footnotes_list %}
+                <li id="footnote-{{ forloop.counter }}">
+                    <div class="[&>p:last-of-type]:inline-block">
+                        {{ footnote.text|richtext }}
+                        &nbsp;<a
+                            href="#footnote-source-{{ forloop.counter }}"
+                            aria-label="Přeskočit na obsah"
+                        >↩</a>
+                    </div>
+                </li>
+            {% endfor %}
+        </ol>
+    </div>
+{% endif %}
diff --git a/home/templatetags/footnotes.py b/home/templatetags/footnotes.py
new file mode 100644
index 0000000..9331bd6
--- /dev/null
+++ b/home/templatetags/footnotes.py
@@ -0,0 +1,42 @@
+from django.template import Library
+import re
+from wagtail.models import Page
+from django.utils.safestring import mark_safe
+
+register = Library()
+
+
+@register.simple_tag(takes_context=True)
+def richtext_footnotes(context, html):
+    """
+    example: {% richtext_footnotes page.body|richtext %}
+
+    html: already processed richtext field html
+    Assumes "page" in context.
+    """
+    FIND_FOOTNOTE_TAG = re.compile(r'<footnote id="(.*?)">.*?</footnote>')
+
+    if not isinstance(context.get("page"), Page):
+        return html
+
+    page = context["page"]
+    if not hasattr(page, "footnotes_list"):
+        page.footnotes_list = []
+    footnotes = {str(footnote.uuid): footnote for footnote in page.footnotes.all()}
+
+    def replace_tag(match):
+        try:
+            index = process_footnote(match.group(1), page)
+        except (KeyError, ValidationError):
+            return ""
+        else:
+            return f'<a href="#footnote-{index}" id="footnote-source-{index}"><sup>[{index}]</sup></a>'
+
+    def process_footnote(footnote_id, page):
+        footnote = footnotes[footnote_id]
+        if footnote not in page.footnotes_list:
+            page.footnotes_list.append(footnote)
+        # Add 1 to the index as footnotes are indexed starting at 1 not 0.
+        return page.footnotes_list.index(footnote) + 1
+
+    return mark_safe(FIND_FOOTNOTE_TAG.sub(replace_tag, html))
diff --git a/institut/settings/base.py b/institut/settings/base.py
index 7330ad8..e2ebb9e 100644
--- a/institut/settings/base.py
+++ b/institut/settings/base.py
@@ -48,6 +48,7 @@ INSTALLED_APPS = [
     "wagtail.images",
     "wagtail.search",
     "wagtail.admin",
+    "wagtail_footnotes",
     "wagtail",
     "modelcluster",
     "taggit",
diff --git a/institut/urls.py b/institut/urls.py
index 1768475..744b08c 100644
--- a/institut/urls.py
+++ b/institut/urls.py
@@ -4,10 +4,12 @@ from django.urls import include, path
 from wagtail import urls as wagtail_urls
 from wagtail.admin import urls as wagtailadmin_urls
 from wagtail.documents import urls as wagtaildocs_urls
+from wagtail_footnotes import urls as footnotes_urls
 
 urlpatterns = [
     path("django-admin/", admin.site.urls),
     path("admin/", include(wagtailadmin_urls)),
+    path("footnotes/", include(footnotes_urls)),
     path("documents/", include(wagtaildocs_urls)),
 ]
 
diff --git a/requirements/base.txt b/requirements/base.txt
index 7a996fc..5d8f90c 100644
--- a/requirements/base.txt
+++ b/requirements/base.txt
@@ -1,6 +1,7 @@
 dj-database-url==2.0.0
 django-environ==0.9.0
+django-taggit==3.1.0
+django-modelcluster==6.0
 psycopg2-binary==2.9.6
 wagtail==5.0.2
-django-taggit==4.0.0
-django-modelcluster==6.0
+wagtail-footnotes==0.10.0
-- 
GitLab