{"id":18831339,"url":"https://github.com/django-cms/djangocms-text","last_synced_at":"2026-05-29T11:00:38.678Z","repository":{"id":227475577,"uuid":"771528408","full_name":"django-cms/djangocms-text","owner":"django-cms","description":"Text Plugin for django CMS","archived":false,"fork":false,"pushed_at":"2026-05-14T06:21:41.000Z","size":19976,"stargazers_count":21,"open_issues_count":4,"forks_count":14,"subscribers_count":3,"default_branch":"main","last_synced_at":"2026-05-14T08:27:33.384Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/django-cms.png","metadata":{"files":{"readme":"README.rst","changelog":"CHANGELOG.rst","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2024-03-13T13:17:13.000Z","updated_at":"2026-05-14T06:21:45.000Z","dependencies_parsed_at":"2024-10-27T11:16:26.484Z","dependency_job_id":"d93a09af-575b-4e70-a0aa-9b851861f5ad","html_url":"https://github.com/django-cms/djangocms-text","commit_stats":null,"previous_names":["django-cms/djangocms-text"],"tags_count":37,"template":false,"template_full_name":null,"purl":"pkg:github/django-cms/djangocms-text","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/django-cms%2Fdjangocms-text","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/django-cms%2Fdjangocms-text/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/django-cms%2Fdjangocms-text/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/django-cms%2Fdjangocms-text/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/django-cms","download_url":"https://codeload.github.com/django-cms/djangocms-text/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/django-cms%2Fdjangocms-text/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33648534,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-05-29T02:00:06.066Z","response_time":107,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-08T01:53:22.105Z","updated_at":"2026-05-29T11:00:38.671Z","avatar_url":"https://github.com/django-cms.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"djangocms-text\n==============\n|pypi| |coverage| |precommit| |python| |django| |djangocms|\n\n``djangocms-text`` is a flexible and extensible rich text editing solution for Django\nCMS. This package is designed as a replacement for ``djangocms-text-ckeditor``,\nintroducing a swappable rich text editor interface and supporting enhanced data\nstorage in both HTML and JSON formats.\n\nFeatures\n--------\n\n- **Swappable editors** — switch between TipTap (default), CKEditor 4, and\n  CKEditor 5 frontends.\n- **HTML and JSON storage** — store content in either format, depending on\n  your use case.\n- **Inline editing** — click a text plugin to edit it directly in edit mode.\n- **Text-enabled plugins** — embed CMS plugins inline within rich text.\n- **Drop-in for djangocms-text-ckeditor** — automatic data migration on\n  install.\n\nInstallation\n------------\n\n1. Install: ``pip install djangocms-text``\n2. Add to ``INSTALLED_APPS``::\n\n       INSTALLED_APPS = [..., \"djangocms_text\", ...]\n\n3. Run migrations: ``python manage.py migrate djangocms_text``\n4. Start your server and add a Text plugin.\n\nThe default editor is TipTap and is included in the base package. To use a\ndifferent editor, install its frontend package and set ``TEXT_EDITOR``:\n\n.. code-block:: python\n\n    INSTALLED_APPS = [..., \"djangocms_text.contrib.text_ckeditor4\", ...]\n    TEXT_EDITOR = \"djangocms_text.contrib.text_ckeditor4.ckeditor4\"\n\n\nEditors\n-------\n\n``djangocms-text`` ships with several editor frontends that can be swapped via\nthe ``TEXT_EDITOR`` setting:\n\n- **TipTap** *(default)* — A modern, modular rich text editor. Supports\n  text-enabled plugins, dynamic links, inline editing, and on-the-fly\n  Markdown conversion. Does not allow direct HTML source editing.\n- **CKEditor 4** — Compatible with ``djangocms-text-ckeditor`` and usable as\n  a drop-in replacement. Supports inline editing and text-enabled plugins.\n- **CKEditor 5** — Available as a `separate package\n  \u003chttps://github.com/django-cms/djangocms-text-ckeditor5\u003e`_\n  (``djangocms-text-ckeditor5``) to keep licenses separated. Fully supports\n  text-enabled CMS plugins and dynamic links.\n\n\nConfiguration\n-------------\n\nSelecting an editor\n~~~~~~~~~~~~~~~~~~~\n\nAdd the editor's package to ``INSTALLED_APPS`` and point ``TEXT_EDITOR`` at\nits ``RTEConfig`` path::\n\n    INSTALLED_APPS = [\n        ...,\n        \"djangocms_text.contrib.text_ckeditor4\",\n        ...,\n    ]\n    TEXT_EDITOR = \"djangocms_text.contrib.text_ckeditor4.ckeditor4\"\n\nGlobal editor configuration\n~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nThe ``TEXT_EDITOR`` setting points to a ``RTEConfig`` object. The following\nattributes are available:\n\n- ``name`` (str) — The name of the RTE configuration.\n- ``config`` (str) — The configuration string.\n- ``js`` (Iterable[str]) — JavaScript files to include.\n- ``css`` (dict) — CSS files to include.\n- ``admin_css`` (Iterable[str]) — CSS files for the admin interface only.\n- ``inline_editing`` (bool) — Whether to enable inline editing.\n- ``child_plugin_support`` (bool) — Whether to support child plugins.\n- ``configuration`` (dict) — Frontend-specific options.\n- ``additional_context`` (dict) — Additional context to pass to the editor.\n\nThe default configuration is:\n\n.. code-block:: python\n\n    DEFAULT_EDITOR = RTEConfig(\n        name=\"tiptap\",\n        config=\"TIPTAP\",\n        js=(\"djangocms_text/bundles/bundle.tiptap.min.js\",),\n        css={\"all\": (\"djangocms_text/css/bundle.tiptap.min.css\",)},\n        admin_css=(\"djangocms_text/css/tiptap.admin.css\",),\n        inline_editing=True,\n        child_plugin_support=True,\n        configuration={},  # see below\n    )\n\nUse ``admin_css`` to include CSS files loaded into the dialog window, e.g. to\ndeclare custom colors or other styles.\n\nFrontend-specific configuration\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nFrontend-specific options live in the ``configuration`` property of the\n``RTEConfig``. The contents depend on the rich text editor frontend (TipTap,\nCKEditor 4, etc.).\n\nThe preferred way to set them is via ``TEXT_EDITOR_SETTINGS``, which mirrors\n``RTEConfig.configuration``. For backwards compatibility with\n``djangocms-text-ckeditor``, ``CKEDITOR_SETTINGS`` is also forwarded to the\nfrontend (even when the frontend is not CKEditor 4).\n\nExample TipTap configuration (matches the defaults):\n\n.. code-block:: python\n\n    DEFAULT_EDITOR.configuration = {\n        \"inlineStyles\": [  # Styles menu, by default contains some rarer styles\n            { \"name\": 'Small', \"element\": 'small' },\n            { \"name\": 'Kbd', \"element\": 'kbd' },\n            { \"name\": 'Var', \"element\": 'var' },\n            { \"name\": 'Samp', \"element\": 'samp' },\n        ],\n        \"blockStyles\": [],\n        # Block styles menu, e.g., for paragraphs; empty by default.\n        # Example: [{\"name\": \"Lead\", \"element\": \"div\", \"attributes\": {\"class\": \"lead\"}}]\n        \"textColors\": {  # Colors offered for the text color menu - the keys are CSS classes\n            'text-primary': {\"name\": \"Primary\"},\n            'text-secondary': {\"name\": \"Secondary\"},\n            'text-success': {\"name\": \"Success\"},\n            'text-danger': {\"name\": \"Danger\"},\n            'text-warning': {\"name\": \"Warning\"},\n            'text-info': {\"name\": \"Info\"},\n            'text-light': {\"name\": \"Light\"},\n            'text-dark': {\"name\": \"Dark\"},\n            'text-body': {\"name\": \"Body\"},\n            'text-muted': {\"name\": \"Muted\"},\n        },\n        \"tableClasses\": \"table\",  # classes added to new tables\n    }\n\nThree ways to configure the classes added to new tables::\n\n    # Option 1: modify the default editor configuration in place\n    from djangocms_text.editors import DEFAULT_EDITOR\n    DEFAULT_EDITOR.configuration[\"tableClasses\"] = \"table ui\"\n\n    # Option 2: offer a list of named choices\n    from djangocms_text.editors import DEFAULT_EDITOR\n    DEFAULT_EDITOR.configuration[\"tableClasses\"] = [\n        [\"table\", _(\"Default\")],\n        [\"table table-striped\", _(\"Striped\")],\n    ]\n\n    # Option 3: use TEXT_EDITOR_SETTINGS in settings.py\n    TEXT_EDITOR_SETTINGS = {\n        \"tableClasses\": \"table ui\",\n    }\n\n\nInline editing\n~~~~~~~~~~~~~~\n\nInline editing lets editors click a text plugin and change its contents\ndirectly in django CMS edit mode. The editor appears around the text field,\nand changes are saved as soon as the field loses focus.\n\nInline editing wraps the HTML in a ``\u003cdiv\u003e`` in edit mode, which may interact\nwith site CSS that uses direct child selectors.\n\nInline editing is enabled by default. To disable it::\n\n    TEXT_INLINE_EDITING = False\n\nWhen enabled, a toolbar toggle lets users switch inline editing on and off\nfor the current session. If only text changes, editing continues seamlessly.\nIf a text-enabled plugin was added, changed, or removed, the page refreshes\nto update the page tree and re-render the affected plugins.\n\nCustom plugin templates\n^^^^^^^^^^^^^^^^^^^^^^^\n\nIf you override the default ``cms/plugins/text.html`` and wrap the plugin\noutput in container elements (e.g. ``\u003csection\u003e``, ``\u003carticle\u003e``, ``\u003cdiv\u003e``),\nthe **innermost** container must have the class ``cms-content-start``::\n\n    \u003c!-- cms/plugins/text.html --\u003e\n    \u003csection class=\"my-text-plugin\"\u003e\n        \u003cdiv class=\"cms-content-start\"\u003e\n            {{ body|safe }}\n        \u003c/div\u003e\n    \u003c/section\u003e\n\nThis tells the inline editor which element to use as the editable area.\nWithout it, the editor will treat the outermost container as the editable\nregion, which may include non-editable markup.\n\n\nText-enabled plugins\n~~~~~~~~~~~~~~~~~~~~\n\ndjangocms-text supports text-enabled plugins (note: not all editor frontends\ndo — see Editors_).\n\nTo make a plugin available inside Text plugins, set ``text_enabled = True``\non its plugin class::\n\n    class MyTextPlugin(TextPlugin):\n        name = \"My text plugin\"\n        model = MyTextModel\n        text_enabled = True\n\nThe plugin then appears in the *CMS Plugins* dropdown (puzzle icon) in the\neditor and is previewed inline.\n\n**Pro-tip**: Provide an ``icon_alt`` method on the plugin so that, when many\n``text_enabled`` plugins are available, users get a useful tooltip — for\nexample, the name of the product whose price the plugin shows.\n\nText-enabled plugins can also have their own icons. Add a ``text_icon``\nproperty containing SVG source code:\n\n.. code-block::\n\n    class MyTextPlugin(TextPlugin):\n        name = \"My text plugin\"\n        model = MyTextModel\n        text_enabled = True\n        text_icon = '\u003csvg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\"\u003e\u003cpath d=\"M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8zm-1-13h2v6h-2zm0 8h2v2h-2z\"/\u003e\u003c/svg\u003e'\n\nThe icon shows in the CMS plugin pulldown menu and toolbar. To make a\ntext-enabled plugin directly accessible from the editor toolbar, add its\nname (e.g. ``\"LinkPlugin\"``) to the toolbar configuration.\n\nFor more on extending the CMS with plugins, see the `django-cms doc`_.\n\n.. _django-cms doc: http://docs.django-cms.org/en/latest/reference/plugins.html#cms.plugin_base.CMSPluginBase.text_enabled\n\n\nDefault content in placeholders\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nYou can use ``TextPlugin`` in ``default_plugins`` (see the\n`CMS_PLACEHOLDER_CONF`_ setting docs). ``TextPlugin`` requires a single\nvalue: ``body`` for the default HTML content. To attach default children\n(e.g. a ``LinkPlugin``), reference them in the body using\n``\"%(_tag_child_\u003corder\u003e)s\"`` placeholders:\n\n.. code-block::\n\n    CMS_PLACEHOLDER_CONF = {\n        'content': {\n            'name' : _('Content'),\n            'plugins': ['TextPlugin', 'LinkPlugin'],\n            'default_plugins':[\n                {\n                    'plugin_type':'TextPlugin',\n                    'values':{\n                        'body':'\u003cp\u003eGreat websites : %(_tag_child_1)s and %(_tag_child_2)s\u003c/p\u003e'\n                    },\n                    'children':[\n                        {\n                            'plugin_type':'LinkPlugin',\n                            'values':{\n                                'name':'django',\n                                'url':'https://www.djangoproject.com/'\n                            },\n                        },\n                        {\n                            'plugin_type':'LinkPlugin',\n                            'values':{\n                                'name':'django-cms',\n                                'url':'https://www.django-cms.org'\n                            },\n                        },\n                    ]\n                },\n            ]\n        }\n    }\n\n.. _CMS_PLACEHOLDER_CONF: https://docs.django-cms.org/en/stable/reference/configuration.html#cms-placeholder-conf\n\n\nHTML sanitizer\n~~~~~~~~~~~~~~\n\n``djangocms-text`` uses `nh3 \u003chttps://nh3.readthedocs.io/en/latest/\u003e`_ to\nsanitize HTML, both for security and to enforce well-formed markup.\nSanitization may strip tags useful for some use cases (e.g. ``iframe``).\nCustomize allowed tags and attributes via ``TEXT_ADDITIONAL_ATTRIBUTES``::\n\n    TEXT_ADDITIONAL_ATTRIBUTES = {\n        'iframe': {'scrolling', 'allowfullscreen', 'frameborder'},\n    }\n\nThe dictionary maps tag names to sets of allowed attribute names.\n\nIf you have settings in the older djangocms-text-ckeditor style using both\n``TEXT_ADDITIONAL_TAGS`` and ``TEXT_ADDITIONAL_ATTRIBUTES``, those will be\ntranslated automatically — with a warning from the Django checks framework\nat server startup.\n\n**Note**: Some frontend editors pre-sanitize content before sending it to\nthe server, which can render the above settings ineffective.\n\nTo disable sanitization entirely, set ``TEXT_HTML_SANITIZE = False``.\n\n\nMarkdown support\n----------------\n\nThe TipTap frontend includes (minimal) Markdown support:\n\n- Markdown is converted to HTML when **pasting**. (To prevent XSS attacks,\n  pasted content is not converted if it contains JavaScript.)\n- When **typing**, Markdown syntax is converted on the fly.\n\nSupported syntax:\n\n- Headings: ``# Heading 1``, ``## Heading 2``, ``### Heading 3``, etc.\n- Bold: ``**bold text**`` or ``__bold text__``\n- Italic: ``*italic text*`` or ``_italic text_``\n- Strikethrough: ``~~strikethrough~~``\n- Links: ``[link text](http://example.com)``\n- Lists: ``- Item`` or ``* Item`` for unordered, ``1. Item`` for ordered.\n- Blockquotes: ``\u003e Quote``\n- Code: ```inline code``` for inline code, triple backticks for code blocks.\n- Tables (pasting only): use ``|`` to separate columns::\n\n    | Header 1 | Header 2 |\n    |----------|----------|\n    | Row 1    | Row 2    |\n\n- Horizontal rules: ``---``\n\n\nOptional contrib extensions\n---------------------------\n\nThree small contrib packages ship with djangocms-text as **demos of how\nto extend the TipTap editor dynamically** — at runtime, through the\n``window.CMS_Editor.tiptap`` registry, without forking djangocms-text\nor pulling a second copy of TipTap into the page. The full registry\ncontract is documented in `tiptap-extensions.md`_.\n\nEach is opt-in: add the relevant app to ``INSTALLED_APPS``. The READMEs\nlinked below cover what each package adds, the configuration knobs it\nexposes, and the integration pattern it illustrates.\n\n- `djangocms_text.contrib.filer_image\n  \u003cdjangocms_text/contrib/filer_image/README.rst\u003e`_ —\n  insert django-filer images via filer's existing picker popup.\n- `djangocms_text.contrib.officepaste\n  \u003cdjangocms_text/contrib/officepaste/README.rst\u003e`_ —\n  strip Word/Outlook detritus on paste.\n\n.. _tiptap-extensions.md: tiptap-extensions.md\n\n\nMigrating from djangocms-text-ckeditor\n--------------------------------------\n\ndjangocms-text's migrations automatically migrate existing text plugins\nfrom djangocms-text-ckeditor and clean up old tables. To migrate:\n\n1. Uninstall ``djangocms-text-ckeditor``.\n2. Remove ``djangocms_text_ckeditor`` from ``INSTALLED_APPS``.\n3. Add ``djangocms_text`` to ``INSTALLED_APPS`` (see Installation_).\n4. Run ``python -m manage migrate djangocms_text``.\n\n**Attention**: The migration also deletes djangocms-text-ckeditor's tables\nfrom the database (to avoid referential integrity issues). Make a backup\nbeforehand to be safe.\n\nSwitching from CKEditor 4 to TipTap\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nWhen transitioning from CKEditor 4 to TipTap (the new default), keep these\npoints in mind:\n\n- **No HTML source editing** — TipTap does not support direct HTML source\n  editing. This simplifies the editor for most users but may be a drawback\n  for advanced users or developers who manually edit HTML.\n- **Loss of non-standard formatting** — TipTap stores content in an\n  abstract JSON format and regenerates HTML on edit. Formatting created via\n  CKEditor 4 plugins or manually added HTML classes may not be preserved.\n  This only happens on first edit after migration.\n- **Keep using CKEditor 4 if needed** — If retaining CKEditor 4 behavior is\n  essential, use the CKEditor 4 backend that ships with djangocms-text.\n\n**You can continue to use CKEditor 4.** Compared to djangocms-text-ckeditor,\nthe CKEditor 4 sources have moved to\n``static/djangocms_text/vendor/ckeditor4``. Update any custom CKEditor 4\nplugins accordingly.\n\n\nUsage outside django CMS\n------------------------\n\ndjango CMS Text can be used without django CMS installed. It provides\n``HTMLField``, ``HTMLFormField``, and the ``TextEditorWidget`` class for use\nwith any Django model or form.\n\nWhen django CMS is not installed alongside django CMS Text, add this to your\n``MIGRATION_MODULES`` setting to skip creation of the CMS plugin model::\n\n    MIGRATION_MODULES = {\n        ...,\n        \"djangocms_text\": None,\n        ...,\n    }\n\n\nContributing\n------------\n\nContributions to ``djangocms-text`` are welcome! See the\n`contributing guidelines \u003chttps://docs.django-cms.org/en/stable/contributing/index.html\u003e`_\nto get started.\n\npre-commit hooks\n~~~~~~~~~~~~~~~~\n\nThe repo uses pre-commit git hooks to ensure code quality. Install with::\n\n    pip install pre-commit\n    pre-commit install\n\nBuilding the JavaScript\n~~~~~~~~~~~~~~~~~~~~~~~\n\n``djangocms-text`` distributes a JavaScript bundle containing the editor\nfrontends and CMS integration. To rebuild the bundle::\n\n    nvm use\n    npm install\n    npm run build\n\nOther build scripts:\n\n* ``npm run build:dev`` — unminified development build\n* ``npm run watch`` — rebuild on file changes\n\n\nAcknowledgments\n---------------\n\nSpecial thanks to the django CMS community and to all contributors to the\n``djangocms-text-ckeditor`` project.\n\nLicense\n-------\n\nThis project is licensed under the BSD-3-Clause License — see the LICENSE\nfile for details.\n\n\n.. |pypi| image:: https://badge.fury.io/py/djangocms-text.svg\n    :target: http://badge.fury.io/py/djangocms-text\n.. |coverage| image:: https://codecov.io/gh/django-cms/djangocms-text/branch/main/graph/badge.svg\n    :target: https://codecov.io/gh/django-cms/djangocms-text\n.. |python| image:: https://img.shields.io/pypi/pyversions/djangocms-text\n    :alt: PyPI - Python Version\n    :target: https://pypi.org/project/djangocms-text/\n.. |django| image:: https://img.shields.io/pypi/frameworkversions/django/djangocms-text\n    :alt: PyPI - Django Versions from Framework Classifiers\n    :target: https://www.djangoproject.com/\n.. |djangocms| image:: https://img.shields.io/pypi/frameworkversions/django-cms/djangocms-text\n    :alt: PyPI - django CMS Versions from Framework Classifiers\n    :target: https://www.django-cms.org/\n.. |precommit| image:: https://results.pre-commit.ci/badge/github/django-cms/djangocms-text/main.svg\n   :target: https://results.pre-commit.ci/latest/github/django-cms/djangocms-text/main\n   :alt: pre-commit.ci status\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdjango-cms%2Fdjangocms-text","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdjango-cms%2Fdjangocms-text","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdjango-cms%2Fdjangocms-text/lists"}