Skip to content

matthiask/django-prose-editor

Repository files navigation

django-prose-editor

Prose editor for the Django admin based on ProseMirror and Tiptap. Announcement blog post.

About rich text editors

Copied from the django-content-editor documentation.

We have been struggling with rich text editors for a long time. To be honest, I do not think it was a good idea to add that many features to the rich text editor. Resizing images uploaded into a rich text editor is a real pain, and what if you’d like to reuse these images or display them using a lightbox script or something similar? You have to resort to writing loads of JavaScript code which will only work on one browser. You cannot really filter the HTML code generated by the user to kick out ugly HTML code generated by copy-pasting from word. The user will upload 10mb JPEGs and resize them to 50x50 pixels in the rich text editor.

All of this convinced me that offering the user a rich text editor with too much capabilities is a really bad idea. The rich text editor in FeinCMS only has bold, italic, bullets, link and headlines activated (and the HTML code button, because that’s sort of inevitable – sometimes the rich text editor messes up and you cannot fix it other than going directly into the HTML code. Plus, if someone really knows what they are doing, I’d still like to give them the power to shot their own foot).

If this does not seem convincing you can always add your own rich text plugin with a different configuration (or just override the rich text editor initialization template in your own project). We do not want to force our world view on you, it’s just that we think that in this case, more choice has the bigger potential to hurt than to help.

Installation

The first step is to ensure that you have an activated virtualenv for your current project, using something like . .venv/bin/activate.

Install the package into your environment:

pip install django-prose-editor

To include nh3 as optional dependency for sanitized HTML, install the extra "sanitize":

pip install django-prose-editor[sanitize]

Add django_prose_editor to INSTALLED_APPS:

INSTALLED_APPS = [
    # ...
    "django_prose_editor",
]

Replace models.TextField with ProseEditorField where appropriate:

from django_prose_editor.fields import ProseEditorField

class Project(models.Model):
    description = ProseEditorField()

Note! No migrations will be generated when switching from and to models.TextField. That's by design. Those migrations are mostly annoying.

Security

ProseMirror does a really good job of only allowing content which confirms to a particular scheme. Of course users can submit what they want, they are not constrainted by the HTML widgets you're using. You should still always sanitize the HTML submitted on the server side. A good way to do this is by using the sanitize argument to the ProseEditorField. You can use the following snippet to always pass HTML through nh3:

from django_prose_editor.sanitized import SanitizedProseEditorField

description = SanitizedProseEditorField()

Install django-prose-editor with the extra "sanitize" to use SanitizedProseEditorField.

Convenience

Sometimes it may be useful to show an excerpt of the HTML field; the ProseEditorField automatically adds a get_*_excerpt method to models which returns the truncated and stripped beginning of your HTML field's content. The name would be Project.get_description_excerpt in the example above.

Customization

NOTE! The previous way of customizing the editor is still supported, but it's not recommended (and documented) anymore.

The editor can be customized using presets; the way to do this is by adding a list of presets to your Django settings:

DJANGO_PROSE_EDITOR_PRESETS = {
    "announcements": {
        "script": "prose-editors/announcements.js",
    },
}

The preset can be selected when instantiating the field:

text = ProseEditorField(_("text"), preset="announcements")

The announcements file is expected to initialize the editor by using the DjangoProseEditor library which is available on the browser window global. The example looks a bit involved, and it is -- but, this unlocks the capability to add project-specific extensions without having to rebuild the editor foundations:

// "announcements" is the name of the preset.
const marker = "data-django-prose-editor-announcements"

function createEditor(textarea) {
  if (textarea.closest(".prose-editor")) return
  const config = JSON.parse(textarea.getAttribute(marker))

  const {
    // Always recommended:
    Document, Dropcursor, Gapcursor, Paragraph, HardBreak, Text,

    // Add support for a few marks:
    Bold, Italic, Subscript, Superscript, Link,

    // A menu is always nice:
    Menu,

    // Helper which knows how to attach a prose editor to a textarea,
    // either textareas which exist already on page load and also those
    // added through the Django admin's inlines mechanism:
    createTextareaEditor,
  } = window.DjangoProseEditor

  const extensions = [
    Document, Dropcursor, Gapcursor, Paragraph, HardBreak, Text,

    Bold, Italic, Subscript, Superscript, Link,

    Menu,
  ]

  return createTextareaEditor(textarea, extensions)
}

window.DjangoProseEditor.initializeEditors(createEditor, `[${marker}]`)

Usage outside the Django admin

The prose editor can easily be used outside the Django admin. The form field respectively the widget includes the necessary CSS and JavaScript:

from django_prose_editor.fields import ProseEditorFormField

class Form(forms.Form):
    text = ProseEditorFormField()

Or maybe you want to use django_prose_editor.widgets.ProseEditorWidget, but why make it more complicated than necessary.

If you're rendering the form in a template you have to include the form media:

<form method="post">
  {{ form.errors }} {# Always makes sense #}
  {{ form.media }}  {# This is the important line! #}
  {{ form.as_div }}
  <button type="submit">send</button>
</form>

Note that the form media isn't django-prose-editor specific, that's a Django feature.

The django-prose-editor CSS uses the following CSS custom properties.

  • --prose-editor-background
  • --prose-editor-foreground
  • --prose-editor-border-color
  • --prose-editor-active-color
  • --prose-editor-disabled-color

If you do not set them, they get their value from the following properties that are defined in the Django admin's CSS:

  • --border-color
  • --body-fg
  • --body-bg
  • --primary

You should set these properties with appropriate values to use django-prose-editor outside the admin in your site.

In addition, you may optionally set a --prose-editor-typographic property to control the color of typographic characters when shown.