Wagtail Tutorial Series:
To get the latest learning resource for Wagtail 4, please check Build Blog With Wagtail CMS (4.0.0)
- Create Wagtail Project
- Dockerizing Wagtail App
- Add Blog Models to Wagtail
- How to write Wagtail page template
- Add Bootstrap Theme to Wagtail
- How to use StreamField in Wagtail
- Wagtail Routable Page
- Add pagination component to Wagtail
- Customize Wagtail Page URL
- Add Full Text Search to Wagtail
- Add Markdown Support to Wagtail
- Add LaTeX Support & Code Highlight In Wagtail
- How to Build Form Page in Wagtail
- How to Create and Manage Menus in Wagtail
- Wagtail SEO Guide
- Source code: https://github.com/AccordBox/wagtail-tailwind-blog
Wagtail Tips:
- Wagtail Tip #1: How to replace ParentalManyToManyField with InlinePanel
- Wagtail Tip #2: How to Export & Restore Wagtail Site
Write style in Wagtail:
- How to use SCSS/SASS in your Django project (Python Way)
- How to use SCSS/SASS in your Django project (NPM Way)
Other Wagtail Topics:
Objective
By the end of this chapter, you should be able to:
- Customize Markdown Renderer
- Add Latex support to the Markdown
Background
In the previous chapter, we have used wagtail-markdown
to help us write content in Markdown and render it to HTML.
The markdown renderer
is built on python-markdown, you can check Github source code to learn more.
We can also add extension to change, extend the behavior of the parser without having to edit the actual source files. Some extensions have been included with python-markdown, you can check the official doc to get more detail. extension
Here we will add extension to make our post page support Latex
.
LaTeX is a document preparation system for high-quality typesetting which includes features designed for the production of technical and scientific documentation
MathJax is an open-source JavaScript display engine for LaTeX, MathML, and AsciiMath notation that works in all modern browsers
So here is the workflow:
- We tell
python-markdown
content in$...$
and$$...$$
isLatex
- We use
MathJax
to display theLatex
content on the browser.
Render Markdown
├── blog
│ ├── __init__.py
│ ├── admin.py
│ ├── apps.py
│ ├── blocks.py
│ ├── md_converter
│ │ ├── __init__.py
│ │ ├── mdx
│ │ │ ├── __init__.py
│ │ │ └── mdx_mathjax.py
│ │ └── utils.py
Notes:
- In
blog
app, we created a Python packagemd_converter
mdx
contains the Markdown extension.- We can create the directory and empty file here and I will talk about them in a bit.
Update blog/md_converter/utils.py
import markdown
def render_markdown(value):
html = markdown.markdown(
value,
extensions=[
'extra',
'codehilite',
'blog.md_converter.mdx.mdx_mathjax',
],
extension_configs={
'codehilite': [
('guess_lang', False),
]
},
output_format='html5'
)
return html
Notes:
- Here we created
render_markdown
function, it would runPython markdown
to render the markdown content to HTML - The
extra
andcodehilite
are built-in extension fromPython markdown
- 'blog.md_converter.mdx.mdx_mathjax' is the custom extension we build for Latex support.
Create blog/md_converter/mdx/mdx_mathjax.py
"""
https://github.com/mayoff/python-markdown-mathjax
"""
import markdown
import html
class MathJaxPattern(markdown.inlinepatterns.Pattern):
def __init__(self, md):
markdown.inlinepatterns.Pattern.__init__(
self, r'(?<!\\)(\$\$?)(.+?)\2', md)
def handleMatch(self, m):
# Pass the math code through, unmodified except for basic entity
# substitutions.
# Stored in htmlStash so it doesn't get further processed by Markdown.
text = html.escape(m.group(2) + m.group(3) + m.group(2))
return self.markdown.htmlStash.store(text)
class MathJaxExtension(markdown.Extension):
def extendMarkdown(self, md, md_globals):
# Needs to come before escape matching because \ is pretty important
# in LaTeX
md.inlinePatterns.add('mathjax', MathJaxPattern(md), '<escape')
def makeExtension(configs=[]):
return MathJaxExtension(configs)
Notes:
- The key point here is the regex expression
(?<!\\)(\$\$?)(.+?)\2
, it would recognize$...$
and$$...$$
, sopython-markdown
would not process the content inside$...$
and$$...$$
. - In most cases, people would like to use
$...$
for inline math and$$...$$
for multi-line math.
Templates
Update blog/templatetags/blogapp_tags.py
from blog.md_converter.utils import render_markdown
@register.filter(name='markdown')
def markdown(value):
return render_markdown(value)
We created a custom Django filter markdown
Update wagtail_bootstrap_blog/templates/blog/components/streamfield.html
{% load static wagtailcore_tags blogapp_tags %}
{% with blocks=page.body %}
{% for block in blocks %}
{% if block.block_type == 'h1' %}
<h1>{{ block.value }}</h1>
{% elif block.block_type == 'h2' %}
<h2>{{ block.value }}</h2>
{% elif block.block_type == 'paragraph' %}
{{ block.value|richtext }}
{% elif block.block_type == 'image_text' %}
{% include 'blog/blocks/image_text.html' with block=block only %}
{% elif block.block_type == 'image_carousel' %}
{% include 'blog/blocks/image_carousel.html' with block=block only %}
{% elif block.block_type == 'markdown' %}
{{ block.value|markdown|safe }}
{% else %}
<section class="block-{{ block.block_type }}">
{{ block }}
</section>
{% endif %}
{% endfor %}
{% endwith %}
Notes:
- For
markdown
type, we use the custommarkdown
filter to process it and render it to HTML. - By default, Django would do
auto-escape
, we can disable this behavior with thesafe
filter. You can check Django doc: safe tag to learn more. - If you want to sanitize html generated from markdown, please check bleach and this code
MathJax
Update wagtail_bootstrap_blog/templates/blog/post_page.html
{% extends "base.html" %}
{% load wagtailcore_tags wagtailimages_tags blogapp_tags static %}
{% block extra_css %}
<link rel="stylesheet" href="{% static "css/code_theme.css" %}" />
{% endblock %}
{% block content %}
{% endblock %}
{% block extra_js %}
{# If you want to use Latex with Markdown #}
<script type="text/x-mathjax-config">
MathJax.Hub.Config({
extensions: ["tex2jax.js"],
jax: ["input/TeX", "output/HTML-CSS"],
tex2jax: {
inlineMath: [['$','$']],
displayMath: [['$$','$$']] ,
processEscapes: true
},
"HTML-CSS": { availableFonts: ["TeX"] }
});
</script>
<script type="text/javascript"
src="//cdn.mathjax.org/mathjax/latest/MathJax.js">
</script>
{% endblock %}
Notes:
- We added
MathJax
code to theextra_js
block. - What you should notice is the
inlineMath
anddisplayMath
in the config section. Let's take a look at the final result.
Wagtail Tutorial Series:
To get the latest learning resource for Wagtail 4, please check Build Blog With Wagtail CMS (4.0.0)
- Create Wagtail Project
- Dockerizing Wagtail App
- Add Blog Models to Wagtail
- How to write Wagtail page template
- Add Bootstrap Theme to Wagtail
- How to use StreamField in Wagtail
- Wagtail Routable Page
- Add pagination component to Wagtail
- Customize Wagtail Page URL
- Add Full Text Search to Wagtail
- Add Markdown Support to Wagtail
- Add LaTeX Support & Code Highlight In Wagtail
- How to Build Form Page in Wagtail
- How to Create and Manage Menus in Wagtail
- Wagtail SEO Guide
- Source code: https://github.com/AccordBox/wagtail-tailwind-blog
Wagtail Tips:
- Wagtail Tip #1: How to replace ParentalManyToManyField with InlinePanel
- Wagtail Tip #2: How to Export & Restore Wagtail Site
Write style in Wagtail:
- How to use SCSS/SASS in your Django project (Python Way)
- How to use SCSS/SASS in your Django project (NPM Way)
Other Wagtail Topics: