martor


Martor is a Markdown Editor plugin for Django, supported for Bootstrap & Semantic-UI.
Features
- Live Preview
- Integrated with Ace Editor
- Supported with Bootstrap and Semantic-UI
- Supported Multiple Fields fixed this issue
- Upload Images to imgur.com (via API) and custom uploader
- Direct Mention users
@[username]
- (requires user to logged in). - Supports embed/iframe video from (Youtube, Vimeo, Dailymotion, Yahoo, Veoh, & Metacafe)
- Spellchecking (only supports US English at this time)
- Emoji
:emoji_name:
+ Cheat sheets - Martor Commands Reference
- Supports Django Admin
- Toolbar Buttons
- Highlight
pre
- Custom ID Attributes (Add custom IDs to any text element using
{#custom-id}
syntax, e.g., # Heading1 {#my-h1-id}
, for easy linking and navigation.
Preview


Requirements
Django>=3.2
Markdown>=3.0
requests>=2.12.4
bleach
Installation
Martor is available directly from PyPI:
1. Installing the package.
$ pip install martor
2. Don't forget to add 'martor'
to your 'INSTALLED_APPS'
setting (without migrations).
INSTALLED_APPS = [
....
'martor',
]
3. Add url pattern to your urls.py.
urlpatterns = [
...
path('martor/', include('martor.urls')),
]
4. Collect martor's static files in your STATIC_ROOT
folder.
./manage.py collectstatic
Setting Configurations settings.py
Please register your application at https://api.imgur.com/oauth2/addclient
to get IMGUR_CLIENT_ID
and IMGUR_API_KEY
.
MARTOR_THEME = 'bootstrap'
MARTOR_ENABLE_CONFIGS = {
'emoji': 'true',
'imgur': 'true',
'mention': 'false',
'jquery': 'true',
'living': 'false',
'spellcheck': 'false',
'hljs': 'true',
}
MARTOR_TOOLBAR_BUTTONS = [
'bold', 'italic', 'horizontal', 'heading', 'pre-code',
'blockquote', 'unordered-list', 'ordered-list',
'link', 'image-link', 'image-upload', 'emoji',
'direct-mention', 'toggle-maximize', 'help'
]
MARTOR_ENABLE_LABEL = False
MARTOR_ENABLE_ADMIN_CSS = True
MARTOR_IMGUR_CLIENT_ID = 'your-client-id'
MARTOR_IMGUR_API_KEY = 'your-api-key'
MARTOR_MARKDOWNIFY_FUNCTION = 'martor.utils.markdownify'
MARTOR_MARKDOWNIFY_URL = '/martor/markdownify/'
MARTOR_MARKDOWNIFY_TIMEOUT = 0
MARTOR_MARKDOWNIFY_TIMEOUT = 1000
MARTOR_MARKDOWN_EXTENSIONS = [
'markdown.extensions.extra',
'markdown.extensions.nl2br',
'markdown.extensions.smarty',
'markdown.extensions.fenced_code',
'markdown.extensions.sane_lists',
'martor.extensions.urlize',
'martor.extensions.del_ins',
'martor.extensions.mention',
'martor.extensions.emoji',
'martor.extensions.mdx_video',
'martor.extensions.escape_html',
"martor.extensions.mdx_add_id",
]
MARTOR_MARKDOWN_EXTENSION_CONFIGS = {}
MARTOR_UPLOAD_URL = ''
MARTOR_UPLOAD_URL = '/martor/uploader/'
MARTOR_SEARCH_USERS_URL = ''
MARTOR_SEARCH_USERS_URL = '/martor/search-user/'
MARTOR_MARKDOWN_BASE_EMOJI_URL = 'https://github.githubassets.com/images/icons/emoji/'
MARTOR_MARKDOWN_BASE_EMOJI_URL = ''
MARTOR_MARKDOWN_BASE_MENTION_URL = 'https://python.web.id/author/'
MARTOR_ALTERNATIVE_JS_FILE_THEME = "semantic-themed/semantic.min.js"
MARTOR_ALTERNATIVE_CSS_FILE_THEME = "semantic-themed/semantic.min.css"
MARTOR_ALTERNATIVE_JQUERY_JS_FILE = "jquery/dist/jquery.min.js"
ALLOWED_URL_SCHEMES = [
"file", "ftp", "ftps", "http", "https", "irc", "mailto",
"sftp", "ssh", "tel", "telnet", "tftp", "vnc", "xmpp",
]
ALLOWED_HTML_TAGS = [
"a", "abbr", "b", "blockquote", "br", "cite", "code", "command",
"dd", "del", "dl", "dt", "em", "fieldset", "h1", "h2", "h3", "h4", "h5", "h6",
"hr", "i", "iframe", "img", "input", "ins", "kbd", "label", "legend",
"li", "ol", "optgroup", "option", "p", "pre", "small", "span", "strong",
"sub", "sup", "table", "tbody", "td", "tfoot", "th", "thead", "tr", "u", "ul"
]
ALLOWED_HTML_ATTRIBUTES = [
"alt", "class", "color", "colspan", "datetime",
"height", "href", "id", "name", "reversed", "rowspan",
"scope", "src", "style", "title", "type", "width"
]
Check this setting is not set else csrf will not be sent over ajax calls:
CSRF_COOKIE_HTTPONLY = False
Usage
Model
from django.db import models
from martor.models import MartorField
class Post(models.Model):
description = MartorField()
Form
from django import forms
from martor.fields import MartorFormField
class PostForm(forms.Form):
description = MartorFormField()
Admin
from django.db import models
from django.contrib import admin
from martor.widgets import AdminMartorWidget
from yourapp.models import YourModel
class YourModelAdmin(admin.ModelAdmin):
formfield_overrides = {
models.TextField: {'widget': AdminMartorWidget},
}
admin.site.register(YourModel, YourModelAdmin)
Template Renderer
Simply safely parse markdown content as html output by loading templatetags from martor/templatetags/martortags.py
.
{% load martortags %}
{{ field_name|safe_markdown }}
# example
{{ post.description|safe_markdown }}
Don't miss to include the required css & js files before use.
You can take a look at this folder martor_demo/app/templates for more details.
The below example is a one of the way to implement it when you choose the MARTOR_THEME = 'bootstrap'
:
{% extends "bootstrap/base.html" %}
{% load static %}
{% load martortags %}
{% block css %}
<link href="{% static 'plugins/css/ace.min.css' %}" type="text/css" media="all" rel="stylesheet" />
<link href="{% static 'martor/css/martor.bootstrap.min.css' %}" type="text/css" media="all" rel="stylesheet" />
{% endblock %}
{% block content %}
<div class="martor-preview">
<h1>Title: {{ post.title }}</h1>
<p><b>Description:</b></p>
<hr />
{{ post.description|safe_markdown }}
</div>
{% endblock %}
{% block js %}
<script type="text/javascript" src="{% static 'plugins/js/highlight.min.js' %}"></script>
<script>
$('.martor-preview pre').each(function(i, block){
hljs.highlightBlock(block);
});
</script>
{% endblock %}
Template Editor Form
Different with Template Renderer, the Template Editor Form have more css & javascript dependencies.
{% extends "bootstrap/base.html" %}
{% load static %}
{% block css %}
<link href="{% static 'plugins/css/ace.min.css' %}" type="text/css" media="all" rel="stylesheet" />
<link href="{% static 'plugins/css/resizable.min.css' %}" type="text/css" media="all" rel="stylesheet" />
<link href="{% static 'martor/css/martor.bootstrap.min.css' %}" type="text/css" media="all" rel="stylesheet" />
{% endblock %}
{% block content %}
<form class="form" method="post">{% csrf_token %}
<div class="form-group">
{{ form.title }}
</div>
<div class="form-group">
{{ form.description }}
</div>
<div class="form-group">
<button class="btn btn-success">
<i class="save icon"></i> Save Post
</button>
</div>
</form>
{% endblock %}
{% block js %}
<script type="text/javascript" src="{% static 'plugins/js/ace.js' %}"></script>
<script type="text/javascript" src="{% static 'plugins/js/mode-markdown.js' %}"></script>
<script type="text/javascript" src="{% static 'plugins/js/ext-language_tools.js' %}"></script>
<script type="text/javascript" src="{% static 'plugins/js/theme-github.js' %}"></script>
<script type="text/javascript" src="{% static 'plugins/js/typo.js' %}"></script>
<script type="text/javascript" src="{% static 'plugins/js/spellcheck.js' %}"></script>
<script type="text/javascript" src="{% static 'plugins/js/highlight.min.js' %}"></script>
<script type="text/javascript" src="{% static 'plugins/js/resizable.min.js' %}"></script>
<script type="text/javascript" src="{% static 'plugins/js/emojis.min.js' %}"></script>
<script type="text/javascript" src="{% static 'martor/js/martor.bootstrap.min.js' %}"></script>
{% endblock %}
Custom Uploader
If you want to save the images uploaded to your storage,
Martor also provides a way to handle this. Please checkout this WIKI
Test Martor from this Repository
Assuming you are already setup with a virtual environment (virtualenv):
$ git clone https://github.com/agusmakmun/django-markdown-editor.git
$ cd django-markdown-editor/ && python setup.py install
$ cd martor_demo/
$ python manage.py makemigrations && python manage.py migrate
$ python manage.py runserver
Checkout at http://127.0.0.1:8000/simple-form/ on your browser.
Martor Commands Reference

Notes
Martor was inspired by these great projects: django-markdownx, Python Markdown and Online reStructuredText editor.