[GrandComicsDatabase/gcd-django] Render Notes consistently with shared sanitized Markdown (PR #769)

3 views
Skip to first unread message

jhunterjActual

unread,
Sep 23, 2026, 1:22:50 PM (13 days ago) Sep 23
to GrandComicsDatabase/gcd-django, Subscribed

Group-membership Notes and several other Notes displays showed raw Markdown or plain text while existing Notes supported formatting. This change applies consistent Markdown rendering across catalog details, related tables, appearance and reprint annotations, collection items, and other Notes displays.

Shared rich_text.html and notes.html partials, a reusable MarkdownColumn, and common block/inline renderers replace duplicated rendering patterns, including existing biographies and descriptions. Source text remains unchanged for exports and revision diffs. Inline annotations retain valid HTML, and rendered content no longer receives duplicate line-break conversion.

Rendering and Markdownx previews share an allowlist sanitizer. Display rendering sanitizes unconditionally before marking HTML safe. This adds nh3>=0.3,<0.4; deployments must install the updated requirements. Supported formatting is retained, while scripts, event handlers, unsafe URL schemes, and arbitrary styles are removed. No database migration or source-data conversion is required.

Validation:

  • 65 focused Notes/reprint tests passed, including formatting, sanitization, inline markup, exports, and page rendering.
  • 70 changed/new templates compiled; changed Python files passed Flake8.
  • Django system checks passed, and the migration dry run found no changes.
  • Local synthetic group, membership, and character pages returned HTTP 200 with formatted Notes; browser checks covered links and layout.
  • Staged diff whitespace check passed.

The full test suite and production content were not validated. Existing Django and pygraph/pkg_resources deprecation warnings are tracked separately.


You can view, comment on, or merge this pull request online at:

  https://github.com/GrandComicsDatabase/gcd-django/pull/769

Commit Summary

  • 31f5b5a Render Notes consistently with shared sanitized Markdown

File Changes

(84 files)

Patch Links:

—
Reply to this email directly, view it on GitHub, or unsubscribe.
Triage notifications, keep track of coding agent tasks and review pull requests on the go with GitHub Mobile for iOS and Android. Download it today!
You are receiving this because you are subscribed to this thread.Message ID: <GrandComicsDatabase/gcd-django/pull/769@github.com>

jhunterjActual

unread,
Sep 23, 2026, 1:25:53 PM (13 days ago) Sep 23
to GrandComicsDatabase/gcd-django, Subscribed
jhunterjActual left a comment (GrandComicsDatabase/gcd-django#769)

I noticed this problem after this edit was discarded: https://www.comics.org/changeset/8521014/compare/

—
Reply to this email directly, view it on GitHub, or unsubscribe.
Triage notifications, keep track of coding agent tasks and review pull requests on the go with GitHub Mobile for iOS and Android. Download it today!

You are receiving this because you are subscribed to this thread.Message ID: <GrandComicsDatabase/gcd-django/pull/769/c5799562802@github.com>

gemini-code-assist[bot]

unread,
Sep 23, 2026, 1:26:05 PM (13 days ago) Sep 23
to GrandComicsDatabase/gcd-django, Subscribed

@gemini-code-assist[bot] commented on this pull request.

Code Review

This pull request refactors the Markdown rendering system by introducing the nh3 library for HTML sanitization and defining separate render_markdown and render_markdown_inline functions to handle block and inline formatting safely. It updates numerous models, template tags, and templates to utilize these functions, and adds a comprehensive test suite. The review feedback highlights several instances where using render_markdown inside list items (

  • ) introduces unwanted block-level paragraph tags and vertical padding, suggesting the use of render_markdown_inline instead to maintain a compact layout.


In apps/gcd/templatetags/credits.py:

> @@ -324,7 +319,7 @@ def __format_credit(story, credit, computed_value='', tailwind=False,
         values = split_reprint_string(credit_value)
         credit_value = '<ul>'
         for value in values:
-            credit_value += '<li>' + esc(value)
+            credit_value += '<li>' + render_markdown(value)

medium

Using render_markdown inside list items (<li>) wraps the content in block-level <p class="pt-4"> tags, which introduces unwanted top padding and margins. Since these are compact list items, consider using render_markdown_inline instead to keep the list layout clean and compact.

⬇️ Suggested change
-            credit_value += '<li>' + render_markdown(value)
+            credit_value += '<li>' + render_markdown_inline(value)

In apps/gcd/templatetags/credits.py:

> +                    reprint_note += ('<li> ' + render_markdown(string) +
+                                     ' </li>')

medium

Using render_markdown inside <li> wraps the reprint notes in block-level <p class="pt-4"> tags, which adds unwanted vertical spacing in the list. Consider using render_markdown_inline to keep the reprint notes compact.

                    reprint_note += ('<li> ' + render_markdown_inline(string) +\n                                     ' </li>')

In apps/gcd/templatetags/credits.py:

> +                    reprint_note += ('<li> ' + render_markdown(string) +
+                                     ' </li>')

medium

Using render_markdown inside <li> wraps the reprint notes in block-level <p class="pt-4"> tags, which adds unwanted vertical spacing in the list. Consider using render_markdown_inline to keep the reprint notes compact.

                    reprint_note += ('<li> ' + render_markdown_inline(string) +\n                                     ' </li>')

In apps/gcd/templatetags/credits.py:

> @@ -877,7 +865,7 @@ def show_reprints(story, bare_value=False):
     if story.reprint_notes:
         for string in split_reprint_string(story.reprint_notes):
             string = string.strip()
-            reprint += '<li> ' + esc(string) + ' </li>'
+            reprint += '<li> ' + render_markdown(string) + ' </li>'

medium

Using render_markdown inside <li> wraps the reprint notes in block-level <p class="pt-4"> tags, which adds unwanted vertical spacing in the list. Consider using render_markdown_inline to keep the reprint notes compact.

⬇️ Suggested change
-            reprint += '<li> ' + render_markdown(string) + ' </li>'
+            reprint += '<li> ' + render_markdown_inline(string) + ' </li>'

In apps/oi/templatetags/compare.py:

> @@ -12,8 +12,9 @@
                                           sum_page_counts, show_barcode, \
                                           show_isbn
 from apps.gcd.templatetags.credits import format_page_count, \
-                                          split_reprint_string, \
-                                          render_markdown
+                                          split_reprint_string
+
+from apps.gcd.markdown_extension import render_markdown

medium

Import render_markdown_inline from apps.gcd.markdown_extension to support rendering reprint notes compactly in the comparison view.

⬇️ Suggested change
-from apps.gcd.markdown_extension import render_markdown
+from apps.gcd.markdown_extension import render_markdown, render_markdown_inline

In apps/oi/templatetags/compare.py:

>      elif field == 'reprint_notes':
         reprint = ''
         if value.strip() != '':
             for string in split_reprint_string(value):
                 string = string.strip()
-                reprint += '<li> ' + esc(string) + ' </li>'
+                reprint += '<li> ' + render_markdown(string) + ' </li>'

medium

Using render_markdown inside <li> wraps the reprint notes in block-level <p class="pt-4"> tags, which adds unwanted vertical spacing in the list. Consider using render_markdown_inline to keep the reprint notes compact.

⬇️ Suggested change
-                reprint += '<li> ' + render_markdown(string) + ' </li>'
+                reprint += '<li> ' + render_markdown_inline(string) + ' </li>'

—
Reply to this email directly, view it on GitHub, or unsubscribe.
Triage notifications, keep track of coding agent tasks and review pull requests on the go with GitHub Mobile for iOS and Android. Download it today!

You are receiving this because you are subscribed to this thread.Message ID: <GrandComicsDatabase/gcd-django/pull/769/review/5294432008@github.com>

jhunterjActual

unread,
Sep 23, 2026, 1:30:14 PM (13 days ago) Sep 23
to GrandComicsDatabase/gcd-django, Push

@jhunterjActual pushed 1 commit.

  • 32f9626 Update apps/gcd/templatetags/credits.py

—
View it on GitHub or unsubscribe.


Triage notifications, keep track of coding agent tasks and review pull requests on the go with GitHub Mobile for iOS and Android. Download it today!

You are receiving this because you are subscribed to this thread.Message ID: <GrandComicsDatabase/gcd-django/pull/769/before/31f5b5a2ce462749b0e9c6247486b541b24bc353/after/32f96266325e7d46cdbd78e866290c7e7643453d@github.com>

jhunterjActual

unread,
Sep 23, 2026, 1:30:39 PM (13 days ago) Sep 23
to GrandComicsDatabase/gcd-django, Push

@jhunterjActual pushed 1 commit.

  • e5e1517 Update apps/gcd/templatetags/credits.py

—
View it on GitHub or unsubscribe.


Triage notifications, keep track of coding agent tasks and review pull requests on the go with GitHub Mobile for iOS and Android. Download it today!

You are receiving this because you are subscribed to this thread.Message ID: <GrandComicsDatabase/gcd-django/pull/769/before/32f96266325e7d46cdbd78e866290c7e7643453d/after/e5e15178d86412a3eb0d11e04af3a42834122f64@github.com>

jhunterjActual

unread,
Sep 23, 2026, 1:30:50 PM (13 days ago) Sep 23
to GrandComicsDatabase/gcd-django, Push

@jhunterjActual pushed 1 commit.

  • 8b1e5b8 Update apps/oi/templatetags/compare.py

—
View it on GitHub or unsubscribe.


Triage notifications, keep track of coding agent tasks and review pull requests on the go with GitHub Mobile for iOS and Android. Download it today!

You are receiving this because you are subscribed to this thread.Message ID: <GrandComicsDatabase/gcd-django/pull/769/before/e5e15178d86412a3eb0d11e04af3a42834122f64/after/8b1e5b883b101219a904a6190bb22d5fe0bd9a2f@github.com>

jhunterjActual

unread,
Sep 23, 2026, 1:31:29 PM (13 days ago) Sep 23
to GrandComicsDatabase/gcd-django, Push

@jhunterjActual pushed 1 commit.

  • e809aa0 Update apps/oi/templatetags/compare.py

—
View it on GitHub or unsubscribe.


Triage notifications, keep track of coding agent tasks and review pull requests on the go with GitHub Mobile for iOS and Android. Download it today!

You are receiving this because you are subscribed to this thread.Message ID: <GrandComicsDatabase/gcd-django/pull/769/before/8b1e5b883b101219a904a6190bb22d5fe0bd9a2f/after/e809aa0e1486d2e2b6d1b4ab9acf205ed355ff30@github.com>

jhunterjActual

unread,
Sep 23, 2026, 1:40:55 PM (13 days ago) Sep 23
to GrandComicsDatabase/gcd-django, Push

@jhunterjActual pushed 1 commit.

  • e0451ef Keep reprint note lists compact and cover rendering paths

—
View it on GitHub or unsubscribe.


Triage notifications, keep track of coding agent tasks and review pull requests on the go with GitHub Mobile for iOS and Android. Download it today!

You are receiving this because you are subscribed to this thread.Message ID: <GrandComicsDatabase/gcd-django/pull/769/before/e809aa0e1486d2e2b6d1b4ab9acf205ed355ff30/after/e0451ef1810a497d7431fd66b70b9857d5d341dd@github.com>

jhunterjActual

unread,
Sep 23, 2026, 1:42:24 PM (13 days ago) Sep 23
to GrandComicsDatabase/gcd-django, Subscribed

@jhunterjActual commented on this pull request.


In apps/gcd/templatetags/credits.py:

> +                    reprint_note += ('<li> ' + render_markdown(string) +
+                                     ' </li>')

Addressed in the applied suggestions and e0451ef. All flagged reprint-note paths now use render_markdown_inline. Six regression cases verify compact markup while preserving formatting, links, paragraph breaks, and directional filtering. The focused run passed 68 tests.

—
Reply to this email directly, view it on GitHub, or unsubscribe.
Triage notifications, keep track of coding agent tasks and review pull requests on the go with GitHub Mobile for iOS and Android. Download it today!

You are receiving this because you are subscribed to this thread.Message ID: <GrandComicsDatabase/gcd-django/pull/769/review/5294624339@github.com>

jhunterjActual

unread,
Sep 23, 2026, 1:42:31 PM (13 days ago) Sep 23
to GrandComicsDatabase/gcd-django, Subscribed

@jhunterjActual commented on this pull request.


In apps/gcd/templatetags/credits.py:

> +                    reprint_note += ('<li> ' + render_markdown(string) +
+                                     ' </li>')

Addressed in the applied suggestions and e0451ef. All flagged reprint-note paths now use render_markdown_inline. Six regression cases verify compact markup while preserving formatting, links, paragraph breaks, and directional filtering. The focused run passed 68 tests.

—


Reply to this email directly, view it on GitHub, or unsubscribe.
Triage notifications, keep track of coding agent tasks and review pull requests on the go with GitHub Mobile for iOS and Android. Download it today!

You are receiving this because you are subscribed to this thread.Message ID: <GrandComicsDatabase/gcd-django/pull/769/review/5294626086@github.com>

JochenGCD

unread,
Sep 25, 2026, 11:27:48 AM (11 days ago) Sep 25
to GrandComicsDatabase/gcd-django, Subscribed

@jochengcd commented on this pull request.


In apps/gcd/models/reprint.py:

> @@ -115,7 +115,9 @@ def get_compare_string(self, base_issue):
                         esc(issue.full_name())))
 
         if self.notes:
-            reprint = '%s [%s]' % (reprint, esc(self.notes))

The notes in a reprint do not use markdown.

—
Reply to this email directly, view it on GitHub, or unsubscribe.
Triage notifications, keep track of coding agent tasks and review pull requests on the go with GitHub Mobile for iOS and Android. Download it today!

You are receiving this because you are subscribed to this thread.Message ID: <GrandComicsDatabase/gcd-django/pull/769/review/5319491876@github.com>

JochenGCD

unread,
Sep 25, 2026, 11:28:30 AM (11 days ago) Sep 25
to GrandComicsDatabase/gcd-django, Subscribed

@jochengcd commented on this pull request.


In apps/gcd/models/story.py:

> @@ -122,12 +124,14 @@ def character_notes(character):
         note = ' (%s)' % note
 
     if character.role:
-        note += ' (%s)' % character.role
+        note += ' (%s)' % (esc(character.role) if html else character.role)

Same here, short notes do not use markdown. Only the fields on the model.

—
Reply to this email directly, view it on GitHub, or unsubscribe.
Triage notifications, keep track of coding agent tasks and review pull requests on the go with GitHub Mobile for iOS and Android. Download it today!

You are receiving this because you are subscribed to this thread.Message ID: <GrandComicsDatabase/gcd-django/pull/769/review/5319499285@github.com>

JochenGCD

unread,
Sep 25, 2026, 11:31:01 AM (11 days ago) Sep 25
to GrandComicsDatabase/gcd-django, Subscribed

@jochengcd commented on this pull request.


In apps/oi/forms/series.py:

> @@ -39,8 +40,8 @@ def __init__(self, *args, **kwargs):
                 super(RuntimeAddSeriesRevisionForm, self).__init__(*args,
                                                                    **kwargs)
                 self.fields['is_singleton'].help_text += \
-                  ' Series notes for an added singleton series will be '\

Two space indentation is correct here. We use four for a logical new line, two for line breaks.

—
Reply to this email directly, view it on GitHub, or unsubscribe.
Triage notifications, keep track of coding agent tasks and review pull requests on the go with GitHub Mobile for iOS and Android. Download it today!

You are receiving this because you are subscribed to this thread.Message ID: <GrandComicsDatabase/gcd-django/pull/769/review/5319523871@github.com>

jhunterjActual

unread,
Sep 25, 2026, 11:34:22 AM (11 days ago) Sep 25
to GrandComicsDatabase/gcd-django, Subscribed

@jhunterjActual commented on this pull request.


In apps/gcd/models/reprint.py:

> @@ -115,7 +115,9 @@ def get_compare_string(self, base_issue):
                         esc(issue.full_name())))
 
         if self.notes:
-            reprint = '%s [%s]' % (reprint, esc(self.notes))

People are currently using the Notes markdown and gcd links in Notes fields where they aren't supported, which makes them display the "raw" markdown and link coding. This was to standardize Notes fields.

—
Reply to this email directly, view it on GitHub, or unsubscribe.
Triage notifications, keep track of coding agent tasks and review pull requests on the go with GitHub Mobile for iOS and Android. Download it today!

You are receiving this because you are subscribed to this thread.Message ID: <GrandComicsDatabase/gcd-django/pull/769/review/5319559062@github.com>

JochenGCD

unread,
Sep 25, 2026, 11:46:40 AM (11 days ago) Sep 25
to GrandComicsDatabase/gcd-django, Subscribed

@jochengcd commented on this pull request.


In apps/gcd/models/reprint.py:

> @@ -115,7 +115,9 @@ def get_compare_string(self, base_issue):
                         esc(issue.full_name())))
 
         if self.notes:
-            reprint = '%s [%s]' % (reprint, esc(self.notes))

I get that.
But these other notes fields are intended to be short and inline.

The example is an unneeded note. In which issue someone joins a group should come from the database (which can be automated), but not a note.

—
Reply to this email directly, view it on GitHub, or unsubscribe.
Triage notifications, keep track of coding agent tasks and review pull requests on the go with GitHub Mobile for iOS and Android. Download it today!

You are receiving this because you are subscribed to this thread.Message ID: <GrandComicsDatabase/gcd-django/pull/769/review/5319681626@github.com>

jhunterjActual

unread,
Sep 25, 2026, 5:09:19 PM (11 days ago) Sep 25
to GrandComicsDatabase/gcd-django, Push

@jhunterjActual pushed 1 commit.

  • a8159ca Preserve plain text in short Notes annotations

—
View it on GitHub or unsubscribe.


Triage notifications, keep track of coding agent tasks and review pull requests on the go with GitHub Mobile for iOS and Android. Download it today!

You are receiving this because you are subscribed to this thread.Message ID: <GrandComicsDatabase/gcd-django/pull/769/before/e0451ef1810a497d7431fd66b70b9857d5d341dd/after/a8159ca6d464a99fb9f84ae423d15588d9f42154@github.com>

jhunterjActual

unread,
Sep 25, 2026, 6:41:22 PM (11 days ago) Sep 25
to GrandComicsDatabase/gcd-django, Subscribed

@jhunterjActual commented on this pull request.


In apps/oi/forms/series.py:

> @@ -39,8 +40,8 @@ def __init__(self, *args, **kwargs):
                 super(RuntimeAddSeriesRevisionForm, self).__init__(*args,
                                                                    **kwargs)
                 self.fields['is_singleton'].help_text += \
-                  ' Series notes for an added singleton series will be '\

Returned to the standard

—
Reply to this email directly, view it on GitHub, or unsubscribe.
Triage notifications, keep track of coding agent tasks and review pull requests on the go with GitHub Mobile for iOS and Android. Download it today!

You are receiving this because you are subscribed to this thread.Message ID: <GrandComicsDatabase/gcd-django/pull/769/review/5323356265@github.com>

jhunterjActual

unread,
Sep 25, 2026, 6:41:39 PM (11 days ago) Sep 25
to GrandComicsDatabase/gcd-django, Subscribed

@jhunterjActual commented on this pull request.


In apps/gcd/models/story.py:

> @@ -122,12 +124,14 @@ def character_notes(character):
         note = ' (%s)' % note
 
     if character.role:
-        note += ' (%s)' % character.role
+        note += ' (%s)' % (esc(character.role) if html else character.role)

Fixed

—
Reply to this email directly, view it on GitHub, or unsubscribe.
Triage notifications, keep track of coding agent tasks and review pull requests on the go with GitHub Mobile for iOS and Android. Download it today!

You are receiving this because you are subscribed to this thread.Message ID: <GrandComicsDatabase/gcd-django/pull/769/review/5323357792@github.com>

jhunterjActual

unread,
Sep 25, 2026, 6:42:07 PM (11 days ago) Sep 25
to GrandComicsDatabase/gcd-django, Subscribed

@jhunterjActual commented on this pull request.


In apps/gcd/models/reprint.py:

> @@ -115,7 +115,9 @@ def get_compare_string(self, base_issue):
                         esc(issue.full_name())))
 
         if self.notes:
-            reprint = '%s [%s]' % (reprint, esc(self.notes))

I see it now. Fixed.

—
Reply to this email directly, view it on GitHub, or unsubscribe.
Triage notifications, keep track of coding agent tasks and review pull requests on the go with GitHub Mobile for iOS and Android. Download it today!

You are receiving this because you are subscribed to this thread.Message ID: <GrandComicsDatabase/gcd-django/pull/769/review/5323359625@github.com>

JochenGCD

unread,
Sep 26, 2026, 4:56:18 AM (10 days ago) Sep 26
to GrandComicsDatabase/gcd-django, Subscribed
jochengcd left a comment (GrandComicsDatabase/gcd-django#769)

Can you please explain and document what the sanitization is doing ?

—
Reply to this email directly, view it on GitHub, or unsubscribe.
Triage notifications, keep track of coding agent tasks and review pull requests on the go with GitHub Mobile for iOS and Android. Download it today!

You are receiving this because you are subscribed to this thread.Message ID: <GrandComicsDatabase/gcd-django/pull/769/c5844777723@github.com>

jhunterjActual

unread,
Sep 26, 2026, 8:45:51 AM (10 days ago) Sep 26
to GrandComicsDatabase/gcd-django, Push

@jhunterjActual pushed 1 commit.

  • 5cd736b Document Markdown sanitization policy and compatibility

—
View it on GitHub or unsubscribe.


Triage notifications, keep track of coding agent tasks and review pull requests on the go with GitHub Mobile for iOS and Android. Download it today!

You are receiving this because you are subscribed to this thread.Message ID: <GrandComicsDatabase/gcd-django/pull/769/before/a8159ca6d464a99fb9f84ae423d15588d9f42154/after/5cd736bba22c2f657d13196e1c165668f33fa9fc@github.com>

jhunterjActual

unread,
Sep 26, 2026, 8:46:50 AM (10 days ago) Sep 26
to GrandComicsDatabase/gcd-django, Subscribed
jhunterjActual left a comment (GrandComicsDatabase/gcd-django#769)

Can you please explain and document what the sanitization is doing ?

Added with 5cd736b

—
Reply to this email directly, view it on GitHub, or unsubscribe.
Triage notifications, keep track of coding agent tasks and review pull requests on the go with GitHub Mobile for iOS and Android. Download it today!

You are receiving this because you are subscribed to this thread.Message ID: <GrandComicsDatabase/gcd-django/pull/769/c5846384100@github.com>

Reply all
Reply to author
Forward
0 new messages