Try   HackMD

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

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
βš™οΈ Q3 2021 the python packages from pypi should be integratable with djangocms-text-ckeditor5 (eg as djangocms-text-ckeditor5-image)
βš™οΈ Q3 2021 implement REST api for the url manager along with the new CMSPlugin'less architecture
βš™οΈ Q3 2021 implement a ckeditor5 es6 plugin for the url manager

Tentative Roadmap

  • 2022 Q2 - the first alpha release (PoC)
  • 2022 Q3 - the beta community release - collect feedback form the community, verify the feasibility of the selected direction
  • 2022 Q3-Q4 - the stable release

Approximate specification

Main points:

  • drop the in-text plugins support, no migration or
  • keep the in-text plugins but allow tight binding with es6 ck5 plugin
  • allow to extend CKEditor5 through its es6 framework
    • 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
    • alternatively, django CMS apps might register extensions which are loaded dynamically (feasibility with webpack DLL to be checked)
  • implement a rest api based url manager plugin
  • implement a rest api based link plugin based on legacy links
  • release as a separate pypi package, eg 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>
  • We need to specify how the link is resolved on published pages where no CKEditor is loaded and just the HTML is rendered. Where does ${url.path} come from?

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
  • the developer adds to his settings.py something as
    ​​​​CKEDITOR5_PLUGINS = [
    ​​​​    'demo_plugin/static/demo-plugin.js',
    ​​​​]
    
    or
  • we offer a ck5 plugin pool allowing the developer to register his plugin
  • ckeditor5 in its render_template renders:
    ​​​​<script src="{% 'djangocms_text_ckeditor/ckeditor5.js'">
    ​​​​<script>
    ​​​​CKEditor.init({
    ​​​​    plugins: {{ settings.CKEDITOR5_PLUGINS }}
    ​​​​})
    ​​​​</script>
    
    or
  • we use data attributes, e.g. of script tags, to send configuration for inline editors, admin editors and HTML fields to the frontend.

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 ckeditor5 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

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

<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

Questions:

Is this a problem? Currently the djangocms-url-manager requires to create an unattached url before using it. It is unclear why a site should not have a set of potential link targets in its url database w/o using them immediately.

This capability requires the rewrite of djangocms-url-manager admin logic in es6. Is this the right place for it?

Any existing in-text plugins that can't be replaced with ckeditor5 es6 framework?

  • Anything non-local that, e.g. uses the context to get information. My favourite example: Footnote plugin that only renders consecutive numbers and stores the content for later rendering in the footer.
  • Also, local information (like links) probably need to resolve their information to html (i.e. content type = Page, id = 7 corresponds to url /this/is/great). This requires interaction with the backend either by in-text plugins or by ajax calls. The latter solution introduces latency for links work / images show.

Pre-MVP launch (Q3)

  • document why we're dropping the plugins (or leave them?)

  • 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 to some extend, we would need to integrate it though
    • CK5 has little but opinionated styling hard coded. This leads to deviations from document stylings. Potential solution: Identification of unwanted CK5 styling and removing it from the site and admin forms using javascript.
    • The missing stylings lead to admin styling in admin forms. To avoid this, the document css needs to be identified and supplied to the ck5 field. Beware of potential gaps between browser default styling on the site and django admin styling in the form where the site styling fails to override.
    example screenshot of a footer editing in CKEditor4

  • Inline editing?
    Image Not Showing Possible Reasons
    • The image file may be corrupted
    • The server hosting the image is unavailable
    • The image path is incorrect
    • The image format is not supported
    Learn More β†’

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 - 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