Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/django-ckeditor/django-ckeditor

Django admin CKEditor integration.
https://github.com/django-ckeditor/django-ckeditor

Last synced: 10 days ago
JSON representation

Django admin CKEditor integration.

Awesome Lists containing this project

README

        

Deprecation notice
==================

I do not recommend using this package anymore since the open source version of CKEditor 4 has unfixed security issues. More on this in `my blog post `__. Alternatives are listed `here on Django Packages `__. I personally am using `django-prose-editor `__. Thanks!

Django CKEditor
===============

.. image:: https://img.shields.io/pypi/v/django-ckeditor.svg
:target: https://pypi.python.org/pypi/django-ckeditor

.. image:: https://img.shields.io/pypi/pyversions/django-ckeditor.svg
:target: https://pypi.org/project/django-ckeditor/

.. image:: https://img.shields.io/pypi/djversions/django-ckeditor.svg
:target: https://pypi.org/project/django-ckeditor/

.. image:: https://github.com/django-ckeditor/django-ckeditor/workflows/Tests/badge.svg
:target: https://github.com/django-ckeditor/django-ckeditor/actions

.. image:: https://readthedocs.org/projects/django-ckeditor/badge/?version=latest&style=flat
:target: https://django-ckeditor.readthedocs.io/en/latest/

**Django admin CKEditor integration.**

Provides a ``RichTextField``, ``RichTextUploadingField``, ``CKEditorWidget`` and ``CKEditorUploadingWidget`` utilizing CKEditor with image uploading and browsing support included.

This version also includes:

#. support to django-storages (works with S3)
#. updated ckeditor to version 4.18.0
#. included all ckeditor language and plugin files to make everyone happy! ( `only the plugins maintained by the ckeditor develops team `__ )

.. contents:: Contents
:depth: 5

Installation
------------

Required
~~~~~~~~
#. Install or add django-ckeditor to your python path.
::

pip install django-ckeditor

#. Add ``ckeditor`` to your ``INSTALLED_APPS`` setting.

#. Run the ``collectstatic`` management command: ``$ ./manage.py collectstatic``. This will copy static CKEditor required media resources into the directory given by the ``STATIC_ROOT`` setting. See `Django's documentation on managing static files `__ for more info.

#. CKEditor needs to know where its assets are located because it loads them
lazily only when needed. The location is determined in the ``ckeditor-init.js``
script. and defaults to ``static/ckeditor/ckeditor/``. This does not work all
the time, for example when using ``ManifestStaticFilesStorage``, any asset
packaging pipeline or whatnot. django-ckeditor is quite good at automatically
detecting the correct place even then, but sometimes you have to hardcode
``CKEDITOR_BASEPATH`` somewhere. This can be hardcoded in settings, i.e.::

CKEDITOR_BASEPATH = "/my_static/ckeditor/ckeditor/"

It is possible to override
the ``admin/change_form.html`` template with your own if you really need to do
this, i.e.::

{% extends "admin/change_form.html" %}

{% block extrahead %}
window.CKEDITOR_BASEPATH = '/my_static/ckeditor/ckeditor/';
{{ block.super }}
{% endblock %}

Of course, you should adapt this snippet to your needs when using
CKEditor outside the admin app.

Required for using widget with file upload
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

#. Add ``ckeditor_uploader`` to your ``INSTALLED_APPS`` setting.

#. Add a ``CKEDITOR_UPLOAD_PATH`` setting to the project's ``settings.py`` file. This setting specifies a relative path to your CKEditor media upload directory. CKEditor uses Django's storage API. By default, Django uses the file system storage backend (it will use your ``MEDIA_ROOT`` and ``MEDIA_URL``) and if you don't use a different backend you have to have write permissions for the ``CKEDITOR_UPLOAD_PATH`` path within ``MEDIA_ROOT``, i.e.::

CKEDITOR_UPLOAD_PATH = "uploads/"

When using default file system storage, images will be uploaded to "uploads" folder in your ``MEDIA_ROOT`` and urls will be created against ``MEDIA_URL`` (``/media/uploads/image.jpg``).

If you want to be able to have control over filename generation, you have to add a custom filename generator to your settings::

# utils.py

def get_filename(filename, request):
return filename.upper()

::

# settings.py

CKEDITOR_FILENAME_GENERATOR = 'utils.get_filename'

CKEditor has been tested with django FileSystemStorage and S3BotoStorage.
There are issues using S3Storage from django-storages.

#. For the default filesystem storage configuration, ``MEDIA_ROOT`` and ``MEDIA_URL`` must be set correctly for the media files to work (like those uploaded by the ckeditor widget).

#. Add CKEditor URL include to your project's ``urls.py`` file::

path('ckeditor/', include('ckeditor_uploader.urls')),

#. Note that by adding those URLs you add views that can upload and browse through uploaded images. Since django-ckeditor 4.4.6, those views are decorated using ``@staff_member_required``. If you want a different permission decorator (``login_required``, ``user_passes_test`` etc.) then add views defined in ``ckeditor.urls`` manually to your urls.py.

Optional - customizing CKEditor editor
--------------------------------------

#. Add a CKEDITOR_CONFIGS setting to the project's ``settings.py`` file. This specifies sets of CKEditor settings that are passed to CKEditor (see CKEditor's `Setting Configurations `__), i.e.::

CKEDITOR_CONFIGS = {
'awesome_ckeditor': {
'toolbar': 'Basic',
},
}

The name of the settings can be referenced when instantiating a RichTextField::

content = RichTextField(config_name='awesome_ckeditor')

The name of the settings can be referenced when instantiating a CKEditorWidget::

widget = CKEditorWidget(config_name='awesome_ckeditor')

By specifying a set named ``default`` you'll be applying its settings to all RichTextField and CKEditorWidget objects for which ``config_name`` has not been explicitly defined ::

CKEDITOR_CONFIGS = {
'default': {
'toolbar': 'full',
'height': 300,
'width': 300,
},
}

It is possible to create a custom toolbar ::

CKEDITOR_CONFIGS = {
'default': {
'toolbar': 'Custom',
'toolbar_Custom': [
['Bold', 'Italic', 'Underline'],
['NumberedList', 'BulletedList', '-', 'Outdent', 'Indent', '-', 'JustifyLeft', 'JustifyCenter', 'JustifyRight', 'JustifyBlock'],
['Link', 'Unlink'],
['RemoveFormat', 'Source']
]
}
}

If you want or need plugins which are not part of django-ckeditor's
plugin set you may specify assets and plugins as follows::

text = RichTextField(
config_name='forum-post',

# CKEDITOR.config.extraPlugins:
extra_plugins=['someplugin'],

# CKEDITOR.plugins.addExternal(...)
external_plugin_resources=[(
'someplugin',
'/static/.../path-to-someplugin/',
'plugin.js',
)],
)

Alternatively, those settings can also be provided through
``CKEDITOR_CONFIGS``.

Optional for file upload
~~~~~~~~~~~~~~~~~~~~~~~~
#. All uploaded files are slugified by default. To disable this feature, set ``CKEDITOR_UPLOAD_SLUGIFY_FILENAME`` to ``False``.

#. Set the ``CKEDITOR_RESTRICT_BY_USER`` setting to ``True`` in the project's ``settings.py`` file (default ``False``). This restricts access to uploaded images to the uploading user (e.g. each user only sees and uploads their own images). Upload paths are prefixed by the string returned by ``get_username``. If ``CKEDITOR_RESTRICT_BY_USER`` is set to a string, the named property is used instead. Superusers can still see all images. **NOTE**: This restriction is only enforced within the CKEditor media browser.

#. Set the ``CKEDITOR_BROWSE_SHOW_DIRS`` setting to ``True`` to show directories on the "Browse Server" page. This enables image grouping by directory they are stored in, sorted by date.

#. Set the ``CKEDITOR_RESTRICT_BY_DATE`` setting to ``True`` to bucked uploaded files by year/month/day.

#. You can set a custom file storage for CKEditor uploader by defining it under ``CKEDITOR_STORAGE_BACKEND`` variable in settings.

#. You can set ``CKEDITOR_IMAGE_BACKEND`` to one of the supported backends to enable thumbnails in ckeditor gallery.
By default, no thumbnails are created and full-size images are used as preview.
Supported backends:

- ``ckeditor_uploader.backends.PillowBackend``: Uses Pillow

#. With the ``PillowBackend`` backend, you can change the thumbnail size with the ``CKEDITOR_THUMBNAIL_SIZE`` setting (formerly ``THUMBNAIL_SIZE``).
Default value: (75, 75)

#. With the ``PillowBackend`` backend, you can convert and compress the uploaded images to jpeg, to save disk space.
Set the ``CKEDITOR_FORCE_JPEG_COMPRESSION`` setting to ``True`` (default ``False``)
You can change the ``CKEDITOR_IMAGE_QUALITY`` setting (formerly ``IMAGE_QUALITY``), which is passed to Pillow:

The image quality, on a scale from 1 (worst) to 95 (best). The default is 75. Values above 95
should be avoided; 100 disables portions of the JPEG compression algorithm and results in
large files with hardly any gain in image quality.

This feature is disabled for animated images.

Usage
-----

Field
~~~~~
The quickest way to add rich text editing capabilities to your models is to use the included ``RichTextField`` model field type. A CKEditor widget is rendered as the form field but in all other regards the field behaves like the standard Django ``TextField``. For example::

from django.db import models
from ckeditor.fields import RichTextField

class Post(models.Model):
content = RichTextField()

**For file upload support** use ``RichTextUploadingField`` from ``ckeditor_uploader.fields``.

Widget
~~~~~~
Alternatively, you can use the included ``CKEditorWidget`` as the widget for a formfield. For example::

from django import forms
from django.contrib import admin
from ckeditor.widgets import CKEditorWidget

from post.models import Post

class PostAdminForm(forms.ModelForm):
content = forms.CharField(widget=CKEditorWidget())
class Meta:
model = Post
fields = '__all__'

class PostAdmin(admin.ModelAdmin):
form = PostAdminForm

admin.site.register(Post, PostAdmin)

**For file upload support** use ``CKEditorUploadingWidget`` from ``ckeditor_uploader.widgets``.

**Overriding widget template**

In Django >=1.11 for overriding ``ckeditor/widget.html`` you have three ways:

#. Place ``ckeditor/widget.html`` in ``BASE_DIR/templates``

- Change ``FORM_RENDERER`` to ``TemplateSettings``.

::

FORM_RENDERER = 'django.forms.renderers.TemplatesSetting'

- Include ``templates`` folder in ``DIRS``

::

TEMPLATES = [{
...
'DIRS': [os.path.join(BASE_DIR, 'templates'), ],
...
}]

- Add ``'django.forms'`` to ``INSTALLED_APPS``.

#. Place ``ckeditor/widget.html`` in ``your_app/templates`` and place ``'your_app'`` **before** ``'ckeditor'`` and ``'ckeditor_uploader'`` in ``INSTALLED_APPS``.

#. Inherit from ``CKEditorWidget`` and override ``template_name`` with a custom template available in TEMPLATES DIRS as defined settings.py.

::

class MyCustomCKEditorWidget(CKEditorWidget):
template_name = "templates/custom_ckeditor/widget.html"

Outside of django admin
~~~~~~~~~~~~~~~~~~~~~~~

When you are rendering a form outside the admin panel, you'll have to make sure all form media is present for the editor to work. One way to achieve this is like this::


{{ myform.media }}
{{ myform.as_p }}

or you can load the media manually as it is done in the demo app::

{% load static %}

When you need to render ``RichTextField``'s HTML output in your templates safely, just use ``{{ content|safe }}``, `Django's safe filter `_

Management Commands
~~~~~~~~~~~~~~~~~~~
Included is a management command to create thumbnails for images already contained in ``CKEDITOR_UPLOAD_PATH``. This is useful to create thumbnails when using django-ckeditor with existing images. Issue the command as follows::

$ ./manage.py generateckeditorthumbnails

**NOTE**: If you're using custom views remember to include ckeditor.js in your form's media either through ``{{ form.media }}`` or through a ```` tag. Admin will do this for you automatically. See `Django's Form Media docs <http://docs.djangoproject.com/en/dev/topics/forms/media/>`__ for more info.

Using S3
~~~~~~~~
See https://django-storages.readthedocs.org/en/latest/

**NOTE:** ``django-ckeditor`` will not work with S3 through ``django-storages`` without this line in ``settings.py``::

AWS_QUERYSTRING_AUTH = False

If you want to use allowedContent
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
To get allowedContent to work, disable **stylesheetparser** plugin.
So include this in your settings.py.::

CKEDITOR_CONFIGS = {
"default": {
"removePlugins": "stylesheetparser",
}
}

Plugins:
--------

django-ckeditor includes the following ckeditor plugins, but not all are enabled by default::

a11yhelp, about, adobeair, ajax, autoembed, autogrow, autolink, bbcode, clipboard, codesnippet,
codesnippetgeshi, colordialog, devtools, dialog, div, divarea, docprops, embed, embedbase,
embedsemantic, filetools, find, flash, forms, iframe, iframedialog, image, image2, language,
lineutils, link, liststyle, magicline, mathjax, menubutton, notification, notificationaggregator,
pagebreak, pastefromword, placeholder, preview, scayt, sharedspace, showblocks, smiley,
sourcedialog, specialchar, stylesheetparser, table, tableresize, tabletools, templates, uicolor,
uploadimage, uploadwidget, widget, wsc, xml

The image/file upload feature is done by the `uploadimage` plugin.

Restricting file upload
-----------------------

#. To restrict upload functionality to image files only, add ``CKEDITOR_ALLOW_NONIMAGE_FILES = False`` in your settings.py file. Currently non-image files are allowed by default.

#. By default the upload and browse URLs use staff_member_required decorator - ckeditor_uploader/urls.py - if you want other decorators just insert two urls found in that urls.py and don't include it.

Demo / Test application
-----------------------

If you clone the repository you will be able to run the ``ckeditor_demo`` application.

#. ``pip install -r ckeditor_demo_requirements.txt``

#. Run ``python manage.py migrate``

#. Create a superuser if you want to test the widget in the admin panel

#. Start the development server.

There is a forms.Form on the main page (/) and a model in admin that uses the widget for a model field.
Database is set to sqlite3 and STATIC/MEDIA_ROOT to folders in temporary directory.

Running selenium test
---------------------

The recommended way to run selenium tests is using tox. Select the appropriate
selenium driver using the ``SELENIUM`` environment variable and optionally
specify that you want to run only one environment since selenium takes some
time and/or since you do not have all supported versions of Python installed
locally. The example uses the combination of Python 3.9 and Django 4.0 which is
a supported combination at the time of writing::

# Either
SELENIUM=firefox tox -e py39-dj40

# Or
SELENIUM=chromium tox -e py39-dj40

# Or even
SELENIUM=firefox tox

Troubleshooting
---------------

If your browser has problems displaying uploaded images in the image upload window you may need to change Django settings:

::

X_FRAME_OPTIONS = 'SAMEORIGIN'

More on https://docs.djangoproject.com/en/1.11/ref/clickjacking/#setting-x-frame-options-for-all-responses

Example ckeditor configuration
------------------------------

::

CKEDITOR_CONFIGS = {
'default': {
'skin': 'moono',
# 'skin': 'office2013',
'toolbar_Basic': [
['Source', '-', 'Bold', 'Italic']
],
'toolbar_YourCustomToolbarConfig': [
{'name': 'document', 'items': ['Source', '-', 'Save', 'NewPage', 'Preview', 'Print', '-', 'Templates']},
{'name': 'clipboard', 'items': ['Cut', 'Copy', 'Paste', 'PasteText', 'PasteFromWord', '-', 'Undo', 'Redo']},
{'name': 'editing', 'items': ['Find', 'Replace', '-', 'SelectAll']},
{'name': 'forms',
'items': ['Form', 'Checkbox', 'Radio', 'TextField', 'Textarea', 'Select', 'Button', 'ImageButton',
'HiddenField']},
'/',
{'name': 'basicstyles',
'items': ['Bold', 'Italic', 'Underline', 'Strike', 'Subscript', 'Superscript', '-', 'RemoveFormat']},
{'name': 'paragraph',
'items': ['NumberedList', 'BulletedList', '-', 'Outdent', 'Indent', '-', 'Blockquote', 'CreateDiv', '-',
'JustifyLeft', 'JustifyCenter', 'JustifyRight', 'JustifyBlock', '-', 'BidiLtr', 'BidiRtl',
'Language']},
{'name': 'links', 'items': ['Link', 'Unlink', 'Anchor']},
{'name': 'insert',
'items': ['Image', 'Flash', 'Table', 'HorizontalRule', 'Smiley', 'SpecialChar', 'PageBreak', 'Iframe']},
'/',
{'name': 'styles', 'items': ['Styles', 'Format', 'Font', 'FontSize']},
{'name': 'colors', 'items': ['TextColor', 'BGColor']},
{'name': 'tools', 'items': ['Maximize', 'ShowBlocks']},
{'name': 'about', 'items': ['About']},
'/', # put this to force next toolbar on new line
{'name': 'yourcustomtools', 'items': [
# put the name of your editor.ui.addButton here
'Preview',
'Maximize',

]},
],
'toolbar': 'YourCustomToolbarConfig', # put selected toolbar config here
# 'toolbarGroups': [{ 'name': 'document', 'groups': [ 'mode', 'document', 'doctools' ] }],
# 'height': 291,
# 'width': '100%',
# 'filebrowserWindowHeight': 725,
# 'filebrowserWindowWidth': 940,
# 'toolbarCanCollapse': True,
# 'mathJaxLib': '//cdn.mathjax.org/mathjax/2.2-latest/MathJax.js?config=TeX-AMS_HTML',
'tabSpaces': 4,
'extraPlugins': ','.join([
'uploadimage', # the upload image feature
# your extra plugins here
'div',
'autolink',
'autoembed',
'embedsemantic',
'autogrow',
# 'devtools',
'widget',
'lineutils',
'clipboard',
'dialog',
'dialogui',
'elementspath'
]),
}
}