Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions manuscript/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -653,7 +653,7 @@ class FolioAdmin(ImportExportModelAdmin):
},
),
("Toponyms", {"fields": ("toponym_list",)}),
("Notes", {"fields": ("folio_includes_map", "folio_notes")}),
("Notes", {"fields": ("folio_includes_map", "folio_notes", "public_note")}),
)

def get_fieldsets(self, request, obj=None):
Expand All @@ -664,7 +664,10 @@ def get_fieldsets(self, request, obj=None):
"Line Range",
{"fields": ("line_code_range_start", "line_code_range_end")},
),
("Notes", {"fields": ("folio_includes_map", "folio_notes")}),
(
"Notes",
{"fields": ("folio_includes_map", "folio_notes", "public_note")},
),
)
return self.fieldsets

Expand Down
32 changes: 32 additions & 0 deletions manuscript/migrations/0129_add_public_notes.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Generated by Django 5.1.15 on 2026-11-03 17:46

import prose.fields
from django.db import migrations


class Migration(migrations.Migration):

dependencies = [
("manuscript", "0128_folio_iiif_canvas"),
]

operations = [
migrations.AddField(
model_name="folio",
name="public_note",
field=prose.fields.RichTextField(
blank=True,
help_text="Shown to readers where this folio begins in the text.",
null=True,
),
),
migrations.AddField(
model_name="singlemanuscript",
name="public_note",
field=prose.fields.RichTextField(
blank=True,
help_text="Shown to readers at the start of the text.",
null=True,
),
),
]
10 changes: 10 additions & 0 deletions manuscript/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -620,6 +620,11 @@ class Folio(models.Model):
validators=[validate_line_number_code],
)
folio_notes = RichTextField(blank=True, null=True)
public_note = RichTextField(
blank=True,
null=True,
help_text="Shown to readers where this folio begins in the text.",
)
manuscript = models.ForeignKey(
"SingleManuscript", on_delete=models.CASCADE, blank=True, null=True
)
Expand Down Expand Up @@ -715,6 +720,11 @@ class SingleManuscript(models.Model):
help_text="The URL to the IIIF manifest for the manuscript. If there isn't one, leave blank.",
verbose_name="IIIF URL",
)
public_note = RichTextField(
blank=True,
null=True,
help_text="Shown to readers at the start of the text.",
)
gazetteer_url = models.URLField(
max_length=255,
blank=True,
Expand Down
6 changes: 6 additions & 0 deletions manuscript/templatetags/stanza_tags.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,12 @@ def annotate_text(html_content, annotations):
return mark_safe("".join(result))


@register.filter
def has_text(html):
"""Whether rich text has any text, since an emptied editor can leave markup"""
return bool(html and BeautifulSoup(html, "html.parser").get_text(strip=True))


@register.filter
def format_ms(manuscript):
"""Format manuscripts for dropdowns as:
Expand Down
46 changes: 46 additions & 0 deletions manuscript/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -789,6 +789,52 @@ def test_folio_holding_only_the_stanza_number(self):
self.assertEqual(self.stanza_dividers(response), [("01.01.00", "1v")])
self.assertEqual(self.line_breaks(response), [("01.01.01", "2r")])

def test_shows_a_folio_s_public_note_at_a_divider(self):
self.add_folios(("1r", "01.01.00", "01.02.08"), ("1v", "01.03.00", "01.04.08"))
Folio.objects.filter(folio_number="1v").update(
public_note="<div>Stanzas appear elsewhere</div>",
folio_notes="Working note",
)

response = self.get_page()

self.assertContains(response, "<div>Stanzas appear elsewhere</div>", count=1)
self.assertContains(response, 'class="public-note"', count=1)
self.assertNotContains(response, "Working note")

def test_shows_a_folio_s_public_note_at_a_mid_stanza_break(self):
self.add_folios(("1r", "01.01.00", "01.02.04"), ("1v", "01.02.05", "01.04.08"))
Folio.objects.filter(folio_number="1v").update(public_note="Mid-stanza note")

self.assertContains(self.get_page(), "Mid-stanza note", count=1)

def test_shows_the_manuscript_s_public_note_at_the_start_of_the_text(self):
self.manuscript.public_note = "Images may not match the text"
self.manuscript.save()
other = SingleManuscript.objects.create(item_id=2, siglum="Test2")

response = self.get_page()

self.assertContains(response, "Images may not match the text", count=1)
html = response.content.decode()
self.assertLess(
html.index("Images may not match the text"), html.index('id="libro-1"')
)
self.assertNotContains(
self.client.get(reverse("manuscript_stanzas", args=[other.siglum])),
"Images may not match the text",
)

def test_leaves_out_public_notes_without_text(self):
self.manuscript.public_note = "<ul><li><br></li></ul>"
self.manuscript.save()
self.add_folios(("1r", "01.01.00", "01.02.08"), ("1v", "01.03.00", "01.04.08"))
Folio.objects.filter(folio_number="1v").update(public_note="<div>&nbsp;</div>")

response = self.get_page()

self.assertNotContains(response, 'class="public-note')


class RepairFolioToponymsTest(TestCase):
def setUp(self):
Expand Down
16 changes: 16 additions & 0 deletions templates/stanzas.html
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,13 @@
display: none !important; /* Optional: Hide title if it's redundant */
}

/* Public notes on a manuscript and on a folio */
.public-note {
margin-bottom: 1rem;
font-size: 0.875rem;
color: #4b5563;
}

/* Hamburger menu styles */
.hamburger-menu {
display: inline-flex;
Expand Down Expand Up @@ -211,6 +218,9 @@ <h3 class="text-4xl font-bold" id="top">
<!-- Text content -->
<div class="w-2/3">
<div class="space-y-4">
{% if default_manuscript.public_note|has_text %}
<div class="public-note">{{ default_manuscript.public_note|safe }}</div>
{% endif %}
{% for book_number, stanza_pairs in paired_books.items %}
<div>
<!-- Book headers -->
Expand All @@ -231,6 +241,9 @@ <h2 id="book-{{book_number}}" class="text-xl font-serif font-bold mb-4 scroll-mt
</div>
<div class="flex-grow border-t border-gray-300"></div>
</div>
{% if stanza_pair.current_folio.public_note|has_text %}
<div class="public-note">{{ stanza_pair.current_folio.public_note|safe }}</div>
{% endif %}
{% endif %}
{% for segment in stanza_pair.segments %}
{% if segment.folio_break %}
Expand All @@ -241,6 +254,9 @@ <h2 id="book-{{book_number}}" class="text-xl font-serif font-bold mb-4 scroll-mt
</div>
<div class="flex-grow border-t border-dashed border-gray-300"></div>
</div>
{% if segment.folio_break.public_note|has_text %}
<div class="public-note">{{ segment.folio_break.public_note|safe }}</div>
{% endif %}
{% endif %}
<div class="grid grid-cols-2 gap-8 {% if forloop.last %}mb-8{% else %}mb-2{% endif %}">
<!-- Original stanza -->
Expand Down
Loading