Skip to content

Commit c457725

Browse files
committed
Fixed #1675 -- Added support for authoring markdown blog entries
1 parent b931a55 commit c457725

File tree

5 files changed

+75
-0
lines changed

5 files changed

+75
-0
lines changed

blog/admin.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
from django.contrib import admin
2+
from django.utils.translation import gettext as _
23

34
from .models import Entry, Event
45

@@ -12,6 +13,8 @@ class EntryAdmin(admin.ModelAdmin):
1213

1314
def formfield_for_dbfield(self, db_field, **kwargs):
1415
formfield = super().formfield_for_dbfield(db_field, **kwargs)
16+
if db_field.name == "content_format":
17+
formfield.help_text = _("Psst, we have markdown now 🤫")
1518
if db_field.name == "body":
1619
formfield.widget.attrs.update(
1720
{
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# Generated by Django 4.2.14 on 2024-10-17 15:08
2+
3+
from django.db import migrations, models
4+
5+
6+
class Migration(migrations.Migration):
7+
8+
dependencies = [
9+
("blog", "0002_event"),
10+
]
11+
12+
operations = [
13+
migrations.AlterField(
14+
model_name="entry",
15+
name="content_format",
16+
field=models.CharField(
17+
choices=[
18+
("reST", "reStructuredText"),
19+
("html", "Raw HTML"),
20+
("md", "Markdown"),
21+
],
22+
max_length=50,
23+
),
24+
),
25+
]

blog/models.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
from django.utils.translation import gettext_lazy as _
1010
from django_hosts.resolvers import reverse
1111
from docutils.core import publish_parts
12+
from markdown import markdown
13+
from markdown.extensions.toc import TocExtension, slugify as _md_title_slugify
1214

1315
BLOG_DOCUTILS_SETTINGS = {
1416
"doctitle_xform": False,
@@ -20,6 +22,11 @@
2022
BLOG_DOCUTILS_SETTINGS.update(getattr(settings, "BLOG_DOCUTILS_SETTINGS", {}))
2123

2224

25+
def _md_slugify(value, separator):
26+
# matches the `id_prefix` setting of BLOG_DOCUTILS_SETTINGS
27+
return "s" + separator + _md_title_slugify(value, separator)
28+
29+
2330
class EntryQuerySet(models.QuerySet):
2431
def published(self):
2532
return self.active().filter(pub_date__lte=timezone.now())
@@ -31,6 +38,7 @@ def active(self):
3138
class ContentFormat(models.TextChoices):
3239
REST = "reST", "reStructuredText"
3340
HTML = "html", "Raw HTML"
41+
MARKDOWN = "md", "Markdown"
3442

3543
@classmethod
3644
def to_html(cls, fmt, source):
@@ -45,6 +53,15 @@ def to_html(cls, fmt, source):
4553
writer_name="html",
4654
settings_overrides=BLOG_DOCUTILS_SETTINGS,
4755
)["fragment"]
56+
if fmt == cls.MARKDOWN:
57+
return markdown(
58+
source,
59+
output_format="html",
60+
extensions=[
61+
# baselevel matches `initial_header_level` from BLOG_DOCUTILS_SETTINGS
62+
TocExtension(baselevel=3, slugify=_md_slugify),
63+
],
64+
)
4865
raise ValueError(f"Unsupported format {fmt}")
4966

5067

blog/tests.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,35 @@ def test_content_format_reST(self):
9494
)
9595
self.assertHTMLEqual(entry.body_html, "<p><strong>test</strong></p>")
9696

97+
def test_content_format_markdown(self):
98+
entry = Entry.objects.create(
99+
pub_date=self.now,
100+
slug="a",
101+
body="**test**",
102+
content_format=ContentFormat.MARKDOWN,
103+
)
104+
self.assertHTMLEqual(entry.body_html, "<p><strong>test</strong></p>")
105+
106+
def test_header_base_level_reST(self):
107+
entry = Entry.objects.create(
108+
pub_date=self.now,
109+
slug="a",
110+
body="test\n====",
111+
content_format=ContentFormat.REST,
112+
)
113+
self.assertHTMLEqual(
114+
entry.body_html, '<div class="section" id="s-test"><h3>test</h3></div>'
115+
)
116+
117+
def test_header_base_level_markdown(self):
118+
entry = Entry.objects.create(
119+
pub_date=self.now,
120+
slug="a",
121+
body="# test",
122+
content_format=ContentFormat.MARKDOWN,
123+
)
124+
self.assertHTMLEqual(entry.body_html, '<h3 id="s-test">test</h3>')
125+
97126

98127
class EventTestCase(DateTimeMixin, TestCase):
99128
def test_manager_past_future(self):

requirements/common.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ docutils==0.20.1
1212
feedparser==6.0.11
1313
Jinja2==3.1.4
1414
libsass==0.23.0
15+
Markdown==3.7
1516
Pillow==10.4.0
1617
psycopg2==2.9.10
1718
Pygments==2.18.0

0 commit comments

Comments
 (0)