Markdown
Omeka S module adding a page block written in Markdown rather than HTML.
The Markdown source is what gets stored, so page content stays readable, diff-able and portable — you can paste it into any other system and it is still the document. The HTML is derived from it when the page is saved.
Why
Omeka's built-in HTML block is fine for prose but poor for anything structured. A table means fighting a WYSIWYG toolbar, and what ends up in the database is HTML nobody would choose to read. A Markdown block stores this:
| Naam | Bouwjaar |
|----------------|----------|
| De Roode Leeuw | 1727 |
The block
Add Markdown to a page. There is one field, and one control under it.
The editor is EasyMDE: a Markdown source editor with a toolbar, keyboard shortcuts, auto-continued lists and a table button. Tick Edit as plain text to strip it back to a bare textarea; your choice is remembered for the next block you open.
Whichever mode you are in, the stored text is the text you typed. The editor never rewrites your Markdown, so syntax it has no button for — footnotes, definition lists — survives editing untouched.
The preview is the page
The preview (👁) and side-by-side (⇄) buttons render your Markdown on the server, using the same converter the published page uses.
This matters more than it sounds. Editors that preview in the browser use a JavaScript parser that knows GitHub-flavoured Markdown, so they agree with the published page about tables and quietly disagree about footnotes and definition lists — precisely the cases you open a preview to check. Here, preview and page cannot differ, because they are the same code.
Supported syntax
Everything in CommonMark, plus the extended syntax for which an upstream extension exists:
| Feature | Example |
|---|---|
| Tables | \| Naam \| Bouwjaar \| |
| Fenced code blocks | ```php |
| Strikethrough | ~~doorgehaald~~ |
| Task lists | - [x] gedaan |
| Automatic URL linking | https://example.org/ |
| Footnotes | tekst[^1] … [^1]: de noot |
| Definition lists | Term on one line, : definitie on the next |
| Heading IDs | ## Molens van Gouda → id="molens-van-gouda" |
Heading IDs are exactly what you would write by hand, because they are what a reader links to.
Footnote IDs are not: CommonMark numbers footnotes from 1 within each block, so two Markdown blocks
on one page would both claim id="fn:1" and the second block's marker would jump to the first
block's note. Footnote IDs therefore carry a short token derived from the block's own text —
id="fn:8a3b565f-1". The token depends only on the Markdown, so the preview, the HTML stored on
save and any later re-render all agree; but do not link to a footnote anchor from outside the page.
Not supported, because CommonMark has no extension for them: emoji shortcodes (:joy:),
==highlight==, ~subscript~ and ^superscript^.
Explicit heading IDs (### Kop {#eigen-id}) and {.class} attributes need the Attributes
extension, which ships disabled — uncomment one line in config/module.config.php to turn it on.
Smart punctuation is there on the same terms.
Raw HTML
Raw HTML in your Markdown is allowed and then sanitised with HTMLPurifier, which is exactly how Omeka's own HTML block behaves, and it honours the same Use HTML purifier setting in global settings.
So <div class="highlight"> survives; <script>, onclick= handlers, javascript: links,
<form> and every form control except the checkbox a task list needs are removed.
Two known and deliberate consequences: role attributes are dropped, and HTML5-only elements
(<figure>, <video>, <section>) are removed, because HTMLPurifier validates against XHTML 1.0.
The core HTML block strips those too.
Reserved IDs
IDs are enabled, so that footnote and heading anchors work. That would otherwise let a block claim
an ID belonging to the page around it — <div id="content">, or simply a heading called
## Content — and leave the theme's own scripts finding the wrong element. So a list of structural
IDs (content, main, header, sidebar, page, and the rest) is reserved: an element asking
for one loses the ID, keeping its text. The legacy name attribute on <a> and <img> goes the
same way. Add your theme's own structural IDs to reserved_ids in config/module.config.php.
Showing Markdown as HTML in your theme
Three ways, from most to least specific.
1. The block's template. The block renders view/common/block-layout/markdown.phtml, which puts
the HTML inside <div class="markdown">. It is a templateable block, so a theme can offer
alternatives — add them to its theme.ini and the editor gets a Template dropdown:
block_templates.markdown.markdown-lead = "Markdown (lead paragraph)"
2. The markdown view helper, for Markdown that lives anywhere else — most usefully a property
value:
<?php echo $this->markdown($item->value('dcterms:description')); ?>
It takes a string or anything stringable, and returns sanitised HTML. Unlike the block it converts on the spot, so use it for a value or two per page rather than across a browse listing.
3. The stylesheet. asset/css/markdown.css is appended automatically and styles only what
themes usually have no rule for — tables, footnotes, definition lists, task lists, fenced code.
Headings, paragraphs and links are left to your theme. Override anything under .markdown.
Installation
Download the release zip, unzip it into Omeka's modules/ directory as Markdown, and install it
from Modules in the admin. No configuration is required and the module has no settings page.
From source, dependencies have to be fetched:
cd /path/to/omeka-s/modules
git clone https://github.com/coret/Omeka-S-module-Markdown.git Markdown
cd Markdown && composer install --no-dev
Requires Omeka S 4.0 or later and PHP 8.1 or later.
What it depends on
Kept deliberately small.
- league/commonmark for the conversion — 8 packages and about 2.4 MB in total, all small, all actively maintained.
- EasyMDE for the editor, vendored in
asset/vendor/easymde/as two files totalling 340 KB. No npm, no build step. - Nothing for sanitising: HTMLPurifier is already one of Omeka's own dependencies.
- Nothing for icons: the toolbar uses the Font Awesome that Omeka's admin theme already loads. EasyMDE is explicitly told not to fetch its own from a CDN, so the admin makes no external requests.
Configuration
There is no settings page. Everything adjustable lives in config/module.config.php under the
markdown key: the list of CommonMark extensions, the options passed to the parser, and the
reserved_ids a block may not claim.
Blocks store their rendered HTML along with a fingerprint of that configuration. Change the extension list and existing blocks notice the mismatch and re-render themselves from their Markdown, rather than serving output produced by a configuration that is gone. Saving a page refreshes its stored HTML.
Development
composer install # includes php-cs-fixer
vendor/bin/php-cs-fixer fix --dry-run --diff # code style, rules copied from Omeka core
Licence
GPL-3.0-or-later. EasyMDE is MIT, league/commonmark is BSD-3-Clause; both licences ship with the code they cover.