Skip to content

feat: extend tiptap table to have a (configurable) class attribute #76

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 25 commits into from
Jun 11, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,14 @@
Changelog
=========

0.9.0 (10-06-2025)
==================
* feat: Add support for pasting Markdown by @fsbraun in https://github.com/django-cms/djangocms-text/pull/100
* feat: extend tiptap table to have a (configurable) class attribute by fsbraun in https://github.com/django-cms/djangocms-text/pull/76

0.8.8 (05-06-2025)
==================
* fix: Text-enabled plugins could not be added in django CMS 3.x
* fix: Text-enabled plugins could not be added in django CMS 3.x by fsbraun in https://github.com/django-cms/djangocms-text/pull/99

0.8.7 (30-05-2025)
==================
Expand Down
160 changes: 102 additions & 58 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -24,29 +24,12 @@ Features
Installation
------------

Install ``djangocms-text`` using pip: ``pip install djangocms-text``.
1. Install: ``pip install djangocms-text``
2. Add to ``INSTALLED_APPS``: ``INSTALLED_APPS = [..., "djangocms_text", ...]``
3. Run migrations: ``python manage.py migrate djangocms_text``
4. Start your server and add a Text plugin!

Build latest development branch using git:

.. code-block:: bash

git clone git@github.com:fsbraun/djangocms-text.git
cd djangocms-text
nvm use
npm install
npx webpack --mode development

You then can install the cloned repo using ``pip install -e
/path/to/the/repo/djangocms-text``.

Finally, add ``djangocms_text`` to your ``INSTALLED_APPS`` in your Django project
settings:

.. code-block:: python

INSTALLED_APPS = [..., "djangocms_text", ...]

Add an editor frontend to your installed apps (if different from the
Optionally, add an editor frontend to your installed apps (if different from the
default TipTap frontend), and set the editor you want to use:

.. code-block:: python
Expand Down Expand Up @@ -91,29 +74,19 @@ project, consider the following points:
provided with djangocms-text. This allows you to retain the familiar
CKEditor4 behavior while benefiting from other updates.

Using ckeditor4
---------------

You can continue to use ckeditor4. Compared to djangocms-text-ckeditor, the
**You can continue to use ckeditor4.** Compared to djangocms-text-ckeditor, the
ckeditor4 sources have moved to ``static/djangocms_text/vendor/ckeditor4``.
Please reflect this if you use custom ckeditor4 plugins.

Usage
-----

After installation, ``djangocms-text`` can be used as your rich text editor in Django
CMS. It can be used as a drop-in for `djangocms-text-ckeditor
<https://github.com/django-cms/djangocms-text-ckeditor>`_. Detailed documentation on
usage and customization will be provided.

Editors
-------

``djangocms-text`` supports multiple rich text editors, which can be swapped out as
needed. The following editors are currently supported:

- **TipTap**: A modern rich text editor with a modular architecture, TipTap is currently
in development and is the default editor. TipTap does not allow the user to edit
in development and is the default editor. It supports text-enabled plugins, dynamic linking,
and conversion of pasted markdown text into HTML. TipTap does not allow the user to edit
HTML directly, which means that some formating options are lost when switching from
CKEditor 4 to TipTap.
- **CKEditor 4**: The initial version of ``djangocms-text`` includes a port of the
Expand All @@ -125,18 +98,6 @@ needed. The following editors are currently supported:
``djangocms-text-ckeditor5`` which provides CKEditor 5 as a rich text editor.


Contributing
------------

Contributions to ``djangocms-text`` are welcome! Please read our contributing guidelines
to get started.

License
-------

This project is licensed under the BSD-3-Clause License - see the LICENSE file for
details.

Configuration
-------------

Expand All @@ -156,8 +117,8 @@ Example::

TEXT_EDITOR = "djangocms_text.contrib.text_ckeditor4.ckeditor4"

Rich text editor configuration
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Rich text editor global configuration
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

The ``TEXT_EDITOR`` setting points to a ``RTEConfig`` object. You can create your custom
``RTEConfig`` instance. The following attributes are available:
Expand All @@ -169,24 +130,94 @@ The ``TEXT_EDITOR`` setting points to a ``RTEConfig`` object. You can create you
- admin_css (Iterable[str]): An iterable of CSS files for the admin interface only.
- inline_editing (bool): Whether to enable inline editing.
- child_plugin_support (bool): Whether to support child plugins.
- configuration (dict): Additional configuration options for the RTE.
- additional_context (dict): Additional context to pass to global editor configuration.

The default configuration is:

.. code-block:: python

RTEConfig(
DEFAULT_EDITOR = RTEConfig(
name="tiptap",
config="TIPTAP",
js=("djangocms_text/bundles/bundle.tiptap.min.js",),
css={"all": ("djangocms_text/css/bundle.tiptap.min.css",)},
admin_css=("djangocms_text/css/tiptap.admin.css",),
inline_editing=True,
child_plugin_support=True,
configuration={}, # Default configuration (see below)
)

You can use the ``admin_css`` attribute to include CSS files that you need to be loaded into the
dialog window, e.g., to declare custom colors or other styles.

Adding configuration to rich text editor frontend
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Configuration to the rich text editor frontend can be passed by adding entries to the
``configuration`` property of the ``RTEConfig``. The contents depends on the rich text
editor frontend (TipTap, CKEditor 4, etc.).

The preferred method to add configuration to rich text editor frontend. Some configuration
can be done using the ``TEXT_EDITOR_SETTINGS`` which is a dictionary that corresponds
to the ``configuration`` property of the ``RTEConfig``. For backwards compatibility with
``djangocms-text-ckeditor``, ``CKEDITOR_SETTINGS`` is also passed on the the rich text
editor frontend (even if it is not CKEditor 4).

Here is an example for Tiptap which represents the default configuration:

.. code-block:: python

# TipTap configuration
DEFAULT_EDITOR.configuration = {
"inlineStyles": [ # Styles menu, by default contains some rarer styles
{ name: 'Small', element: 'small' },
{ name: 'Kbd', element: 'kbd' },
{ name: 'Var', element: 'var' },
{ name: 'Samp', element: 'samp' },
],
"blockStyles": [],
# Block styles menu, e.g., for paragraphs, etc.; empty by default
# Example entry: [{"name": "Lead", "element": "div", "attributes": {"class": "lead"}},]
"textColors": { # Colors offered for the text color menu - the keys are CSS classes
'text-primary': {name: "Primary"},
'text-secondary': {name: "Secondary"},
'text-success': {name: "Success"},
'text-danger': {name: "Danger"},
'text-warning': {name: "Warning"},
'text-info': {name: "Info"},
'text-light': {name: "Light"},
'text-dark': {name: "Dark"},
'text-body': {name: "Body"},
'text-muted': {name: "Muted"},
},
"tableClasses": "table", # Classes added to new(!) tables
}

Here's an example to configure the classes which should be added to new tables::

# Option 1:
# Modify the default editor configuration and point the ``TEXT_EDITOR`` setting to it
from djangocms_text.editors import DEFAULT_EDITOR

DEFAULT_EDITOR.configuration["tableClasses"] = "table ui"

# Option 2:
# Modify the default editor configurartion to offer choices to the editor
from djangocms_text.editors import DEFAULT_EDITOR

DEFAULT_EDITOR.configuration["tableClasses"] = [
["table", _("Default")],
["table table-striped", _("Striped")],
]

# Option 3:
# Both of the above can be replaced adding TEXT_EDITOR_SETTINGS to your settings.py
TEXT_EDITOR_SETTINGS = {
"tableClasses": "table ui",
}


Inline editing feature
~~~~~~~~~~~~~~~~~~~~~~

Expand Down Expand Up @@ -333,7 +364,6 @@ rendering the above settings useless.

To completely disable the feature, set ``TEXT_HTML_SANITIZE = False``.


Usage outside django CMS
------------------------

Expand All @@ -355,35 +385,49 @@ This will prevent the creation of the model for the django CMS text plugin.
Markdown-support
----------------
The TipTap frontend supports some (minimal) Markdown support:

* Markdown is converted to HTML when **pasting**. (To prevent XXS attacks, the
pasted content might not be converted if it contains javascript scritps.)
* When typing, **some** markdown syntax is converted on the fly, e.g., headings, bold, lists


Development
===========
Contributing
------------

Contributions to ``djangocms-text`` are welcome! Please read our
`contributing guidelines <https://docs.django-cms.org/en/stable/contributing/index.html>`_
to get started.

pre-commit hooks
----------------
~~~~~~~~~~~~~~~~

The repo uses pre-commit git hooks to run tools which ensure code quality.

To utilise this, run ``pip install pre-commit`` and then ``pre-commit install``.

Building the JavaScript
-----------------------
~~~~~~~~~~~~~~~~~~~~~~~

``djangocms-text`` distributes a javascript bundle required for the plugin to work,
which contains frontend editors themselves and all the necessary plugins for functioning
within CMS. To build the bundle you need to have to install dependencies with
``nvm use``, ``npm install`` and then to run ``npx webpack``.
``nvm use``, ``npm install`` and then to run ``npx webpack``::

$ nvm use
$ npm install
$ npx webpack

Acknowledgments
---------------

- Special thanks to the Django CMS community and all contributors to the
``djangocms-text-ckeditor`` project.
Special thanks to the Django CMS community and all contributors to the
``djangocms-text-ckeditor`` project.

License
-------

This project is licensed under the BSD-3-Clause License - see the LICENSE file for
details.


.. |pypi| image:: https://badge.fury.io/py/djangocms-text.svg
Expand Down
2 changes: 1 addition & 1 deletion djangocms_text/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,4 @@
10. Github actions will publish the new package to pypi
"""

__version__ = "0.8.8"
__version__ = "0.9.0"
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// CKEDITOR_BASEPATH is setting for CKEditor 4 which reveals the base path from which to load config etc.
// It can be configred by the TEXT_CKEDITOR_BASEPATH setting in the Django settings file.
// It can be configured by the TEXT_CKEDITOR_BASEPATH setting in the Django settings file.
// It's loaded in this script since we avoid inline scripts (to better support CSP headers) and it needs to be
// set before the CKEditor script is loaded which in turn needs to be loaded before the integration script.

Expand Down
47 changes: 25 additions & 22 deletions djangocms_text/editors.py
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,7 @@ def default(self, obj):
"title": _("Merge/split cells"),
"icon": '<svg version="1.1" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg" fill="currentColor"><path d="m2.5 1a1.5 1.5 0 0 0-1.5 1.5v15a1.5 1.5 0 0 0 1.5 1.5h4.459v-1h-4.959v-16h16v4.8203h1v-4.3203a1.5 1.5 0 0 0-1.5-1.5z" style="opacity:.6"/><path d="m7.5614 6.3318h11.006a1.1006 1.1121 0 0 1 1.1006 1.1121v11.121a1.1006 1.1121 0 0 1-1.1006 1.1121h-11.006a1.1006 1.1121 0 0 1-1.1006-1.1121v-11.121a1.1006 1.1121 0 0 1 1.1006-1.1121zm-0.36687 0.74142v11.863h11.74v-11.863z" opacity=".6" style="opacity:1;stroke-width:.73757"/><path d="m6.7285 13v-1h-4.7285v1z" style="opacity:.6"/><path d="m6.7285 8v-1h-4.7285v1z" style="opacity:.6"/><path d="m12 6.6816h1v-4.6816h-1z" style="opacity:.6"/><path d="m7 2v4.6816h1v-4.6816z" style="opacity:.6"/></svg>',
},
"tableClass": {},
"Code": {
"title": _("Code"),
"icon": '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-code" viewBox="0 0 16 16">\n'
Expand Down Expand Up @@ -446,7 +447,8 @@ class RTEConfig:
admin_css (Iterable[str]): An iterable of CSS files for the admin interface only.
inline_editing (bool): Whether to enable inline editing.
child_plugin_support (bool): Whether to support child plugins.
additional_context (dict): Additional context to pass to the editor's global setting.
configuration (dict): A dictionary to hold additional globalboal editor configurations.
additional_context (dict): Additional context to pass to the editor's global setting object.
"""

def __init__(
Expand All @@ -458,6 +460,7 @@ def __init__(
admin_css: Iterable[str] | None = None,
inline_editing: bool = False,
child_plugin_support: bool = False,
configuration: dict | None = None,
additional_context: dict | None = None,
):
""" """
Expand All @@ -468,6 +471,7 @@ def __init__(
self.admin_css = admin_css or ()
self.inline_editing = inline_editing
self.child_plugin_support = child_plugin_support
self.configuration = configuration or {}
self.additional_context = additional_context or {}

def process_base_config(self, base_config: dict) -> dict:
Expand All @@ -479,6 +483,15 @@ def process_base_config(self, base_config: dict) -> dict:
"""
return base_config

def get_base_config(self) -> dict:
"""
Returns the base configuration for the editor.

:return: The base configuration for the editor.
:rtype: dict
"""
return self.process_base_config(_EDITOR_TOOLBAR_BASE_CONFIG.copy())


configuration = {}

Expand Down Expand Up @@ -515,25 +528,15 @@ def get_editor_config(editor: str | None = None) -> RTEConfig:
return configuration[config_name]


def get_editor_base_config(editor: str | None = None) -> dict:
"""
Returns the base configuration for the editor.

:return: The base configuration for the editor.
:rtype: dict
"""
editor_config = get_editor_config(editor)
return editor_config.process_base_config(_EDITOR_TOOLBAR_BASE_CONFIG.copy())


register(
RTEConfig(
name="tiptap",
config="TIPTAP",
js=("djangocms_text/bundles/bundle.tiptap.min.js",),
css={"all": ("djangocms_text/css/bundle.tiptap.min.css",)},
admin_css=("djangocms_text/css/tiptap.admin.css",),
inline_editing=True,
child_plugin_support=True,
)
DEFAULT_EDITOR = RTEConfig(
name="tiptap",
config="TIPTAP",
js=("djangocms_text/bundles/bundle.tiptap.min.js",),
css={"all": ("djangocms_text/css/bundle.tiptap.min.css",)},
admin_css=("djangocms_text/css/tiptap.admin.css",),
inline_editing=True,
child_plugin_support=True,
configuration={},
)

register(DEFAULT_EDITOR)
3 changes: 2 additions & 1 deletion djangocms_text/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,15 @@
# See http://docs.cksource.com/ckeditor_api/symbols/CKEDITOR.config.html
# for all settings

CKEDITOR_SETTINGS: dict[str, Union[str, list]] = {
TEXT_EDITOR_SETTINGS: dict[str, Union[str, list]] = {
"language": "{{ language }}",
"toolbar": "CMS",
"skin": "moono-lisa",
"baseFloatZIndex": 8888888,
"toolbarCanCollapse": False,
"removePlugins": ["flash"],
**getattr(settings, "CKEDITOR_SETTINGS", {}),
**getattr(settings, "TEXT_EDITOR_SETTINGS", {}),
}

TEXT_SAVE_IMAGE_FUNCTION = getattr(settings, "TEXT_SAVE_IMAGE_FUNCTION", None)
Expand Down
Loading