Skip to main content

Markdown

By Bob Coret Adds a page block written in Markdown rather than HTML. Extended syntax — tables, footnotes, definition lists, task lists — with an editor that previews exactly what the page will show, and a toggle back to plain text.
Download 1.0.0

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 Goudaid="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.

Version Released Minimum Omeka version
1.0.0August 11, 2026 [info]^4.0.0