Workgroup - Django CMS CKEditor5 Technical Brief
=============================================================================
###### tags: `workgroup`
## Workgroup Goals
- integrate ckeditor5 modern & reliable UX
- allow to extend it with the new ckeditor5 es6 framework
- allow to create flexible es6 extentions for ckeditor5 instead of the old in-text plugins
- implement the new url management system (based on https://github.com/divio/djangocms-url-manager/)
- rest api based
- allow to link custom models, eg apphooks (blog articles, events, etc)
- automatically handle redirects
## Links
- PR (draft) - https://github.com/django-cms/djangocms-text-ckeditor/pull/551/files
- https://django-cms-ckeditor5-stage.us.aldryn.io/en/
- login: test@django-cms.org
- password: test@django-cms.org
- https://control.divio.com/control/6231/edit/88939/ (for access ask Victor)
## Milestones
| status | date | description |
| ------ | ---------- | ----------- |
| ✅ | 10.12.2020 | deploy a demo project to divio with plain ckeditor5 integration |
| ✅ | 17.12.2020 | create a demo ckeditor5 framework es6 plugin and add it to the PR |
| ✅ | 21.12.2020 | specify the djangocms-url-manager integration |
| ⚙️ | Q1 2021 | the python packages from pypi should be integratable with djangocms-text-ckeditor5 (eg as djangocms-text-ckeditor5-image) |
| ⚙️ | Q1 2021 | implement REST api for the url manager along with the new CMSPlugin'less architecture |
| ⚙️ | Q1 2021 | implement a ckeditor5 es6 plugin for the url manager |
## Tentative Roadmap
- 2021 Q1 - the first alpha release (PoC)
- 2021 Q2 - the beta community release - collect feedback form the community, verify the feasibility of the selected direction
- 2021 Q3 - the stable release
## Approximate specification
Main points:
- drop the in-text plugins support, no migration
- allow to extend CKEditor5 through [its es6 framework](https://ckeditor.com/docs/ckeditor5/latest/framework/guides/overview.html)
- for example a dev can:
- copy-past webpack.config.js
- add custom action items, plugins, toolbars buttons, etc
- build it and put the compiled app in `static/djangocms_text_ckeditor5/app.js`
- run `python manage.py collectstatic` which would override the default `static/djangocms_text_ckeditor5/app.js` from pypi
- implement a rest api based url manager
- release as a separate pypi package, eg [djangocms-text-ckeditor5](https://github.com/django-cms/djangocms-text-ckeditor5)
### api draft ideas
- GET `/api/urls/types/` returns a list of `django.contrib.contenttypes.models.ContentType` instances that can be attached to urls
- or maybe not content types, because we also need to have types as `phone`, `email`, etc
- POST `/api/url/`, which would create a new `Url` model instance by accepting
- `type` - either a content type or a Enum type as `Type.PHONE`, `Type.EMAIL`
- `instance_id`, eg cms.Page.id
- `label` - the text label to be rendered
- a custom ckeditor5 es6 plugin would fetch the required `Url` django model instance by id from the respective endpoint. To implement that we can store the link code in html as following `<a data-url-id="35" data-plugin-type="django-url-manager" href="${url.path}">label from ckeditor</a>`
standarization:
- /api/url/{id} GET
- /api/url/{id} POST
- /api/url/{id} DELETE
- /api/url/{id} PATCH
which django model fields do we want standarize:
- is_attached_to_cms_plugin: bool = False
And then we might have a cronjob that drops the ghost models, eg `Url.objects.filter(is_attached_to_cms_plugin=False).delete()` - but this will drop the Url instancesa that an editor might intend to save in the next few minutes
### es6 plugins integration proposition
- ckeditor-build with `npm run build` a dist ckeditor.js blob and push it to pypi
- the developer runs pip install djangocms-ckeditor-text
- the developer creates a custom es6 ckeditor5 plugin in his project and compiles it into `demo_plugin/static/demo-plugin.js`
- we're planning to allow that using webpack DDL + ckeditor 5 integration - https://github.com/ckeditor/ckeditor5/issues/8395
- the developer adds to his `settings.py` something as
```python
CKEDITOR5_PLUGINS = [
'demo_plugin/static/demo-plugin.js',
]
```
- ckeditor5 in its `render_template` renders:
```html
<script src="{% 'djangocms_text_ckeditor/ckeditor5.js'">
<script>
CKEditor.init({
plugins: {{ settings.CKEDITOR5_PLUGINS }}
})
</script>
```
##### plugins integration use cases
1. installing djangocms-text-ckeditor5 from pypi should work out of the box without webpack build
2. installing third-party djangocms-text-ckeditor5 plugins from pypi (eg as djangocms-text-ckeditor5-image) should work without webpack build - by adding it to settings.py
3. creation of a custom es6 ckeditor5 plugin
- which would have to use a webpack/esbuilt/rollup builder, since it would require ckedito5 es6 framework
- Alex: it seems to be possible to connect custom es6 plugins to ckeditor5 framework to webpack DLL system without requiring a specific builder (eg webpack)
- Andrew: the case #3 would be as important as #2
#### links for dynamic loading of plugins
init a compiled ckeditor5 by selector - https://ckeditor.com/docs/ckeditor5/latest/builds/guides/integration/advanced-setup.html
A ticket about loading plugins with a library function - https://github.com/ckeditor/ckeditor5/issues/8395
```html
<script src="../build/ckeditor.js"></script>
<script>
CKEDITOR.ClassicEditor
.create( document.querySelector( '#classic-editor' ) )
.catch( err => {
console.error( err.stack );
} );
CKEDITOR.InlineEditor
.create( document.querySelector( '#inline-editor' ) )
.catch( err => {
console.error( err.stack );
} );
</script>
```
### edge cases
#### ghost models
Our case of potential django "ghost" models:
1. user opens a new ckeditor5 creation modal
2. user adds a url es6 plugin which creates an instance with `Url.objects.create(pk=1, is_attached_to_cms_plugin=False)`
3. user closes the tab
4. the `Url` models instance stays attached to nothing and we need to define a cleaning routine to get rid of it
#### any existing in-text plugins that can't be replaced with ckeditor5 es6 framework?
- ...
## Pre-MVP launch (Q3)
- document why we're dropping the plugins
- WYSIWYG correct styling - ie when the resulting plugin is rendered with a specific background and fonts, the WYSIWYG editing window should render it respectively. It has been implemented for CKEditor4 already, we would need to integrate it though
<details>
<summary>example screenshot of a footer editing in CKEditor4</summary>

</details>
## Post-MVP Launch (Optional Features)
- image plugin
- localization
- the documentation for the developers who would like to upgrade from ckeditor 4 to 5
- [Restricetive editing](https://ckeditor.com/docs/ckeditor5/latest/features/restricted-editing.html) - we can try to use it eg when only few areas of a plugin are supposed to be editable. Up until now we would move those fields to a django model as CharFields, but restrictive editing has the potential to resolve it.
- IE11 support