volto
volto copied to clipboard
POC Decoupled WYSIWYG Editor UI for Plone Headless
PLIP (Plone Improvement Proposal)
Responsible Persons
Proposer: Dylan Jay
Seconder: Jefferson Bledsoe
Abstract
As a integrator building a website I can:
- write a frontend however I want using whatever frontend framework I want and hosted separately to the Admin UI (can do that now with Plone RESTApi)
- Have that same frontend rendered side by side the Admin UI (with Volto I can’t do that now without using React and customising Volto)
- Still have everything work in WYSIWYG way where I can select blocks, edit them inline (maybe) or in the sidebar, DND them, add new blocks etc.
StoryBlok, using its iframe bridge, shows how this can be achieved. The goal is to see if this can be reverse engineered and applied on top of the volto codebase to see if a similar Decoupled Headless experience is possible.
The end result will not be as nice as UI for editing as Volto is now (but close). But in exchange Plone will be useful to a lot more different integrators because they won’t be required to use React or Volto for their frontend and they won’t have to pay the cost of upgrading their frontend every time improvements are made to the Admin UI.
Motivation
Think of a CMS site as three parts
- Backend and Database
- Admin UI (in plone sometimes called the CMS UI)
- Frontend (or theme if its monolith)
You can connect these three parts architecturally in different ways (“Integrator” used below refers to the developer who is tasked with building a website).
-
Monolith
- Single code base with no clear separation of backend, frontend and Admin UI.
- No RESTApi or graphql. Server-side only.
- The integrator must customise and therefore learn the code frontend, backend and Admin UI to make the a final site.
- The integrator must deploy a customised version of backend, Admin UI and Frontend of the original CMS codebase.
-
Hybrid (coupled)
- Frontend can be done from scratch because a web api like a RESTApi does exist
- However the Admin UI and backend are still monolith (drupal/wordpress/plone classic)
- OR if they are separated by a web API, they are still coupled by assumptions that code on the admin UI needs to be customised to make the editing UX work or you will need to use a particular framework or particular components to make the Admin UI work (Plone Volto)
- The integrator will likely have to deploy a custom version of Admin UI.
-
Headless (decoupled)
- the admin UI is designed to not require customisations and to have less assumptions so it can be used as is, for many scenarios, many frontend frameworks
- the integrator doesn't have to know anything about the Admin UI, backend code, or deploy a custom version of it.
- The integrator deploys only their own frontend based on any framework. Backend and Admin UI can be SaaS or deployed uncustomized somewhere else.
Headless/decoupled is what is growing in popularity. Why?
- freedom of choice: Not everyone wants to use React. You can deliver the same content to non web channels like mobile or social media.
-
Upgrade cost: The Admin UI can be upgraded independent of the frontend. There is no pressure to upgrade a theme to get new CMS editor features. (This is a pain point with Volto integrators now)
- in fact this enables the CMS UI and backend to be cloud hosted as would seldom need to be customised with code
- Lower learning curve. if you know a frontend framework you don't have to learn the CMS framework or code. Just the web apis such as RESTApi.
- lower risk. If I know a headless CMS has been used with a lot of different frameworks and I know my framework of choice then the chance of my project getting bogged down trying to fight the framework is lower.
- BYO HTML/Design systems: if lots of frontend code already exists (like a design system implementation) then build and maintenance is easier. You won't have to rebuild this existing code or rip it apart to retro fit it.
- faster sites: with smaller bundle sizes by separating frontend and editor UI code
- SaaS: you have the option to not have to deal with deploy and scaling databases and the Admin UI.
We want to grow Plone. Plone has good features, good security and good UX with Volto but it has always had a slow learning curve. Headless CMS has a fast learning curve for many frontend developers because you can reuse existing knowledge and are not forced to learn the CMS way. But almost all Headless CMS’s have less enterprise features and a worse editor UX. StoryBlok shows you can get close to Volto style UX while still being a decoupled Headless CMS. If we combine the method StoryBlok uses to achieve this (iframe bridge + visual editor) we can have a decoupled Headless CMS with a lower learning curve and a good editor UX, yet is still open source with flexibility around how it's hosted. This would be unique and attractive to integrators we don't currently have.
I acknowledge that a decoupled editor helps the integrators we don’t currently have more than anyone currently invested in Volto. This is a challenge for finding money/clients/time to invest in this effort. However the reasons that should motivate current Volto core contributors are: Upgrade cost - A new version of Volto with new Admin UI features requires a non trivial upgrade for any non-trivial site. Slow rate of bug fixes - Without new interest we don’t have as many contributions to maintain and improve the code and this makes it more expensive for everyone. Backwards compatibility concerns are making it much harder to get contributions to improve the Admin UI. In some ways this is similar to python 3 vs python2. As Larry Hastings said in his 2014 keynote: “Python 3 is designed for the developers we want in the future, not the ones we have”.
Note: SDK and reusable components would make Plone headless easier for some but a good decoupled admin UI is needed for any Headless CMS with or without reusable components so it's not incompatible with the reusable components idea.
Assumptions
- because StoryBlok can do most of this then it should be possible.
- That headless CMS is indeed the future of CMS.
Proposal & Implementation
To create a Proof of Concept that using the same techniques of an iframe bridge, that StoryBlok uses, it’s possible to have a version of Volto that looks and feels similar but in a decoupled way where the frontend a integrator would create is not Volto and not even React.
The editor layout would look almost the same as current Volto. However the centre will be replaced by an iframe which renders separately hosted frontend (preview).
We take the iframe bridge idea from storyblok, now renamed storyblok bridge which gives a integrator a way to make custom blocks in any framework but still have them linked to the Admin UI, allowing the blocks to be clicked and dragged around, as well as WYSIWYG editing.
You can see the UI of StoryBlok is similar to Volto. However page preview in the centre is an iframe and the block selection and action bar is rendered on top by the iframe bridge and Admin UI.
WARNING: The following will not make sense if you don't read storyblok bridge intro first and storyblok bridge details.
There would be two apis
- Plone REST API (wouldn't require any changes)
- iFrame Bridge API
- The integrator's frontend (called preview) is displayed within an iframe inside the Admin UI.
- This api is a communication channel between the Admin UI and the integrator’s frontend
- The bridge is used for passing messages between the frontend and the Admin UI, such as which block was selected, deleting blocks and dragging blocks.
- The bridge is also used for rendering additional UI elements ontop of the integrator’s frontend, such as highlighting the currently selected block and the blocks action bar (which has the drag handle, the delete block button, etc.).
The following are the levels of integration the integrator would be offered in a final solution. The key point is that the integrator can do the least effort and get a lower UX for their editors for low budget projects. OR the integrator can do more work, and in exchange get a better UX. So they have freedom of choice and Plone becomes accessible to more uses.
Note: All the levels are provided for reference but for the POC the goal is to only concentrate on the riskier parts - Levels 2 & 3 (and maybe 4).
Level 1. Preview reloads via RESTApi (out of scope of POC)
- Uses the iframe bridge for rendering and the REST API only for data fetching.
- your frontend code can be deployed in a completely separate host to the admin UI. Netlify for example.
- When editing a page there is no point and click on blocks or inline editing. All changes are made in the sidebar and after a change the data is re-fetched from the backend and the iframe is reloaded so the preview is up to date.
- This implies any change to a block that currently can only be done inline, has to have an alternative way to do the same thing in the sidebar. This is actually good with accessibility anyway.
- e.g. blocks are added and moved via tree control in the sidebar (which has already been experimented with)
- NB: this means we will formally introduce recursive containment. Blocks can be inside blocks which can be inside blocks.
- The integrator building the site only needs to use the REST API
- Potentially this could be made nicer by enhancing the current RESTApi so during editing a temporary version of the object can be polled using the RESTApi so updates could be more frequent.
Level 2. Point and click blocks and quanta toolbar
- Integrator includes some iframe bridge code in their frontend.
- Integrator adds additional markup to their HTML to link their code with the relevant blocks. see how StoryBlok uses comments for this.
- The iframe bridge uses this markup to handle click events, drawing selection borders and toolbars on a selected block that allows things like adding blocks before or after, cut and paste, navigating to the parent block etc.
- Now an editor can click to select a block and get a block actions toolbar (quanta toolbar) to manage block on the rendered page itself
- the toolbar will support containment of blocks. I.e. you can navigate to the parent and insert inside or after a container block.
- An additional data attribute can be used to indicate a field. So clicking on this will reveal where to edit this field in the sidebar.
- Another data attribute can be used if the integrator wants to add a button to their inline frontend block, to add or remove a subblock or subfields. Or they can call the iframe bridge api from JS code.
- DND of blocks can now happen in the preview iframe by indicating with a line on hover where the block will fit between.
- We would still require a full reload of the preview for each change so would be slow UX.
Level 3. Live updated preview
- Uses the iFrame Bridge. see StoryBlok bridge but the frontend is only listening to changes from the Admin UI JS.
- integrator integrates change events so their frontend components are updated when a block is changed in the sidebar (going via the iframe bridge). The integrator now has to change the state of the changed blocks in real time.
- Now the editor sees the page changing as it's being edited via the sidebar. No full reloads of the preview in the iframe are needed since changes are being sent directly inside the browser now, not via the backend so the RESTApi is no longer used during editing.
- All changes are stored in the browser in the Admin UI state and only sent to the backend on save.
Level 4. Inline Editing (stretch goal of POC)
- The integrator chooses to integrate an additional iframe bridge api for some blocks which lets the user edit the content of the block data inline in their custom component and change events are sent from the frontend blocks to Admin UI via the iframe bridge.
- Note: StoryBlok doesn't yet seem to support something like this
- It should be possible to make a slate like UI work using the same techniques the Block toolbar in Level 2,
- The integrator would use a data attribute on a div with rich text in to indicate this text should be made inline editable and where to store the result.
- The iframe bridge client code would then turn this into a contenteditable and send change and selection events over the iframe bridge.
- The slate like toolbar would be rendered by the Admin UI at the correct position and send back any text changes to make any shortcut codes like for bullets work as they do now.
- This way, if it works, means the slate like toolbar can be extended by the Admin UI code without requiring any changes to the frontend.
- If this doesn’t work there are other options
- The integrator uses a 3rd party rich text component and just sends the rich text changes to the Admin UI via the iframe bridge. This could be supplied by Plone.
- The integrator calls the iframe bridge to render a rich text input overlaid at a point in the page. This won’t be exactly WYSIWYG since the font and wrap won’t match but it is minimal effort on the integrators part.
- There are other inline editing features of Volto that would be nice to be make possible. For example
- Clicking to create a new subcontainer, such an extra control to add a new tab. The block action bar would make this possible but the integrator might want to make the edit interface even nicer. They can send a “add sub block” message via the bridge to make this happen.
- uploading an image or file. This can be handled in a similar way to slate where the Admin UI is sent a message to render an overlay to handle the upload overtop of the frontend block, and a message comes back with the field changed and the url to the image.
Final code (roadmap)
Volto style customization of building on top of out the box blocks could still be supported alongside the Headless mode.
Volto would be split into two separate code bases designed to be deployed to separate servers.
- A reference implementation of a frontend (called Volto) which can be built on top of and themed. But this can be easily replaced. It will be made of reusable components.
- If the integrator doesn’t want to build on top of Volto they can choose to use another framework or use another React framework and still use some Volto components if they want.
- An Admin UI which is deployed as is and rarely needs to be customised. This could be built in anything but still has a customisation story. Initially based on Volto code.
- Block schemas would be made more rich so the need for custom components in the sidebar is less. A more full featured set of widgets and definition of interaction between widgets (like hidewhens) would be able to be defined using json only. This makes the job of the integrator easier as they won’t have to code as often to make custom blocks.
- Eventually this would be turned into a TTW Block schema editor so the integrator can define the block schemas without any deployment at all. For example if the Admin UI was SaaS.
- Block schemas would be made more rich so the need for custom components in the sidebar is less. A more full featured set of widgets and definition of interaction between widgets (like hidewhens) would be able to be defined using json only. This makes the job of the integrator easier as they won’t have to code as often to make custom blocks.
- The iframe bridge code which is included in the frontend and links the two. This also provides parts of the UI rendered inside the iframe such as block selections.
Addons would now come as three packages, Admin UI, backend/api, and Volto frontend.
- For those choosing to use the Volto frontend then this is plug and play.
- For those choosing to bring their own frontend, they will still get the sidebar customisations and backend customisations from the addon. They will also get documentation on how to convert a custom component into something inline editable with the decoupled visual editor. For example, lets say its a slider block addon. A 3rd party slider component could have data attributes to indicate where to allow inline editing of a slides title, and where the button to add or delete a slide are.
- Addon developers will have to ensure that all editing that can be done inline can also be done in the sidebar so the headless intrator can choose not to make their block inline if they don’t want to.
Deliverables for GSOC POC
The goal of the POC is to try to get a functional editing experience as possible in a decoupled way but not to produce a polished api or mature code base. The expectation is that whatever code produced would be rewritten based on what was learnt from the POC.
Plan
- Admin UI. Take the Volto code base and override the editing pane to use an iframe. Rewrite to remove the need to interact directly with rendered blocks
- Frontend. Easiest is to use Volto itself since it already has blocks and already can load full pages from the RestAPI. Alter the code so a user can be logged in but not render the left or right editing UI. Use a few basic blocks to begin with.
- Start a new package for the iframe bridge which can be include in the frontend but is agnostic to whatever framework the frontend is written in
- make the admin UI track url changes in the iframe to update the toolbars for the currently loaded page
- make the contents view open pages in the iframe
- make the edit button reload the current page with an editing argument so the page can load the iframe bridge and make save/cancel switch it back.
- write iframe bridge code to turn a frontend data attribute with the block id into a clickable block that opens the block settings in the sidebar and puts a selection square around the block (level 2)
- handle sending block and page changes in the sidebar to the frontend and having this change the page state in realtime. (Level 3)
- add code to render a block actions menu that can add and remove a block and select the parent block (level 2)
- the bar would be rendered by the Admin UI overtop of the iframe. It would disappear on scroll and reappear once scrolling has stopped.
- handle binding to a div with a data attribute indicating an inline rich text editable field. Turn this into a contenteditable and add text formatting buttons to the block actions bar when text is selected. (level 4)
- handle sending back text changes to the inline text field without it effecting the cursor.
- handle adding buttons to a block in edit mode to add a subblock. Like adding a tab to a tab block, or a row to a table. (level 4)
- handle shortcuts being typed inside an editable field, including enter to create a new text block and slash to change the current block.
- Document the api and the steps an integrator would have to follow to make their own frontend editable.
- Write a report on recommended final api, lessons learnt and what would be needed for a final version
- Create a demo video.
Additional steps
- Try to achieve the same with a frontend written in NextJS, Vue or another framework.
Risks
There seems to be an extended interest for this, if I understand correctly from this: https://community.plone.org/t/plone-as-headless-cms-story-interest-group/18885
@djay sorry for not coming back to you before.
I have some doubts, let me ask some questions inlining them, as I also weigh in on some matters:
Abstract
As a developer building a website I can
- write a component however I want using whatever frontend framework I want (can do that now with Plone/api)
What is for you a "component"? Are we taking about a React component? After a first read of the PLIP I don't have it clear. Also what is "Plone/api"? The RESTAPI, as an interface? the internal Plone api? The components API (props:returned rendered HTML)?
- Have that same component rendered in the editor UI (can’t do that now without using React)
Without React? how? in the editor UI? Then what you'll have in the editor UI? How would you model it? and then render it? As a blocks setting form? as a block editor? If so, a direct consequence would the that inline editing is no longer possible. Maybe you mean with the iframe, reaching to the "View" framework/deployment/development of that hypothetical site?
- Still have everything work in WYSIWYG
ie the Headless CMS usecase of BYO frontend, still get a nice admin/editor UI that is decoupled from the frontend. All a developer has to learn is the api only, nothing about the code of the CMS components.
So the detached view mode working in another framework would receive the updates, let's say, via websockets? Then update itself accordingly? NextJS claims to have such a thing, but not sure if a solution cross platform/build is possible. Meaning, possible most probably is. But having the bandwidth/resources to implement this is a project on its own.
Why decoupled?
- lower learning curve. if you know a frontend framework you don't have to learn Volto. Just the api.
Which API?
- if lots of frontend code already exists (like a design system implementation) then build and maintenance is easier. This will have the change less
Agree, this is part of the new theming story that we are pushing nowadays. Bring your own design system to your public site.
- The CMS UI can be upgraded independent of the frontend. There is no pressure to upgrade a theme to get new CMS editor features. (This is a current pain point with Volto developers now)
Fully agree. The monolithic edit/view approach in the same place is a thing from the past, and it's not aligned on how the modern web works. In terms of performance, bundle size and others, it's just wrong.
- in fact this enables the CMS UI and backend to be cloud hosted as would seldom need to be customised with code
Even allows you the option to not have to deploy the edit bundle somewhere. In some scenarios, the editors could have the CMSUI build locally and build the site from their computers.
- faster sites by separating frontend and editor UI code and freedom of choice of framework.
- We want to grow plone. A storyblok style editor but in an open source enterprise CMS that can be self hosted would be unique and attractive to developers we don't currently have. React is not the only framework.
Indeed.
Note: SDK and reusable components would make plone headless easier for some but a good decoupled admin UI is needed for any Headless CMS with or without reusable components so it's not incompatible and Volto currently is not decoupled. Volto as an CMS UI requires you to create custom volto blocks using the Volto framework and deploy them to the CMS.
You can model a block without thinking on how it's going to be displayed, skipping completely the view side. However, I can't see a practical implementation. Could you elaborate a use case? Eg. a slider block. You'll need to provide a view implementation, at least, along with the block model. A complete different thing is that someone wants to create a complete different slider block view based on your base model, which is ok. Add-ons are complete by default, and have sensible defaults so integrators expect at least one reference implementation.
There's still another dimension that I can think of. Block Styling in the block model. Did you think about it?
- because StoryBlok can do most of this then it should be possible.
We can't compare a VC-backed startup with the development power of the Plone community. We don't have the resources nor the moneys for that. It should be possible, but we have to keep in mind our limitations.
Take Volto and turn it into something like StoryBlok. Either has a mode of current volto or as a fork. This would be a proof of concept only. If it works well then it could replace the current Volto editor weather your decide to use volto for your frontend or not.
Volto is Plone CMS reference implementation for the so called modern frontend technologies, and it's what it is. Volto follows the same monolithic approach that the ClassicUI approach, and we developed it in the image and likeness of ClassicUI. We cannot change it overnight, nor push for such a fundamental change, because lots of existing projects rely on it being like is it now. The Plone Community don't have the output nor the need for that. So I'd say it's out of the question. It has to be developed aside Volto, because conceptually it's another thing.
- Custom block types are created in the admin UI with a schema, similar to TTW content types editor. This is just defining the settings and storage for the block. Their frontend then takes the page with the block definitions and renders them using whatever kind of component they want.
Where would that model be saved? in the backend? What benefits would that bring? aside from allow the users create blocks on the fly. How the view part would adapt to a new field? How would it render it in the intended way? You'll have to adapt both sides still, right?
- your frontend code can be deployed in a completely different url the admin UI. Netlify for example.
This is good, and what we are aiming for, by all means.
- When editing a page there is no point and click on blocks or inline editing. All changes are made in the sidebar and atter a change the iframe is reloaded so the preview is up to date.
As said before this is challenging, and not clear how this will happen in an agnostic (bring your own frontend) way.
- blocks are added, moved via tree control in the sidebar
I fully agree, I'd also like to remove the D&D from the main interface. It has no point any more. I love the Elementor navigator.
- Only the restapi needs to implemented by the developer.
What needs to be implemented by the integrator then?
Live updates (sidebar -> preview)
- Developer integrates change events so their frontend components are updated when a block is changed in the sidebar (going via the iframe bridge). The developer now has to change the state of the changed blocks in real time.
- Now editor sees page changing as it's being edited via the side bar.
Same as above, this is challenging.
Point and click, DND (overlay -> preview)
. Developer adds special block markup when edit mode event received via iframe bridge
- CMS puts layer over rendered iframe to intercept clicks and allows block selection etc.
- Now editor can click to select a block and get a quanta toolbar to manage block on the rendered page itself
- DND of blocks can now happen in the preview iframe
At first read, this seems to me from another world... an iframe, tracking scrolling, mouse events inside it forwarded outside? Maybe StoryBlock has gurus that are able to do this, no doubt about it...
Inline Editing (preview -> sidebar)
The developer chooses to implement an additional api for some blocks which lets the user edit the content of the block directly in the preview page and change events are sent from the components to sidebar via the iframe bridge. On save this is send back via the restapi in the normal way. The editor might reusable component like slate or use completely different code.
This StoryBlock bridge, it's kind of magical, isn't it?
Deliverables
See how close you can get to the full inline editing experience but decoupled. Design easy to implement apis to achieve this.
We need more down to earth deliverables, especially if we want to present this PLIP as a GSOC project, so young student candidates, totally new to Plone can understand what we want and what we want to accomplish with it. We need a more detailed set of requirements as well. I am not opposed to explore this using a GSOC project, I'm only pointing out that it can be daunting, both for the student and the mentor.
@djay You know that I'm very much in favor of making this a reality. It's the way to go, and it would be amazing making this idea a reality. I will help in everything that I can. However, it cannot be taken lightly, and it should be thought and taking into account the vision, the current scenario and the Plone Community resources and make decisions accordingly. There are a lot of other moving parts that we want to address and all of them have to play well together towards the common goal. We have learned that lesson the hard way by having to implement Volto in a rush. Now we know what we did wrong, and how we would do it better. Let's do it better this time. Rushing through things does not work, and every single piece of the puzzle has to play well according the master plan. The final architecture has to make sense and sum of them has to be a symphony, if you allow me the metaphor.
As always, I'd be glad to discuss it in the upcoming interest group meetings. I'd say since it's a complex matter, let's talk about it face to face, and not engage in a back and forth GH powered discussion.
@sneridagh we;ve made some changes to make it clearer for you. Please re-read all of it before replying.
@sneridagh I've responded to some of your comments below to try and address some of your points (in addition to the updated PLIP). I'm happy to dive deeper into anything, but we're still piecing together some of the implementation details.
Abstract
As a developer building a website I can
- write a component however I want using whatever frontend framework I want (can do that now with Plone/api)
What is for you a "component"? Are we taking about a React component? After a first read of the PLIP I don't have it clear. Also what is "Plone/api"? The RESTAPI, as an interface? the internal Plone api? The components API (props:returned rendered HTML)?
Component was possible the wrong term to use here. We're just talking about being agnostic to the frontend technology used to build the interface. The mention of plone/api here is both the REST API and @plone/client
as these (in theory, but @plone/client currently is React only) allow you to access the data coming from Plone in any way you want.
- Have that same component rendered in the editor UI (can’t do that now without using React)
Without React? how? in the editor UI? Then what you'll have in the editor UI? How would you model it? and then render it? As a blocks setting form? as a block editor? If so, a direct consequence would the that inline editing is no longer possible. Maybe you mean with the iframe, reaching to the "View" framework/deployment/development of that hypothetical site?
The idea is that you can attach a plone listener
to your frontend. This listener lets you get the data from plone for view and edit modes. Using it in view mode is no different to using @plone/client
right now. In edit mode, you would also render the editing UI (We control the editing UI. This is basically the Volto editing interface like the toolbar, the sidebar, etc.) and have a 'live updating' version of the content from Plone so that any changes made in the sidebar are automatically passed to the plone listener
and the UI gets updated that way. This is very similar to [preview mode] (https://nextjs.org/docs/pages/building-your-application/configuring/preview-mode) with Next.JS.
- Still have everything work in WYSIWYG
ie the Headless CMS usecase of BYO frontend, still get a nice admin/editor UI that is decoupled from the frontend. All a developer has to learn is the api only, nothing about the code of the CMS components.
So the detached view mode working in another framework would receive the updates, let's say, via websockets? Then update itself accordingly? NextJS claims to have such a thing, but not sure if a solution cross platform/build is possible. Meaning, possible most probably is. But having the bandwidth/resources to implement this is a project on its own.
See the response above about the Next.JS preview mode. This is the bit that needs fleshing out the most as the 'live updating editor' is potentially a significant amount of work.
Why decoupled?
- lower learning curve. if you know a frontend framework you don't have to learn Volto. Just the api.
Which API?
The plone listener
API in the scenario outlined above, but this could be as simple as forwarding REST API response downwards and so the answer to "Which API" is then "The REST API"
- if lots of frontend code already exists (like a design system implementation) then build and maintenance is easier. This will have the change less
Agree, this is part of the new theming story that we are pushing nowadays. Bring your own design system to your public site.
While yes @plone/components
lets you bring your own CSS and do theming using CSS custom properties and use it how you want, you're still tied to writing it with react-aria
and so are forced to use React.
Note: SDK and reusable components would make plone headless easier for some but a good decoupled admin UI is needed for any Headless CMS with or without reusable components so it's not incompatible and Volto currently is not decoupled. Volto as an CMS UI requires you to create custom volto blocks using the Volto framework and deploy them to the CMS.
You can model a block without thinking on how it's going to be displayed, skipping completely the view side. However, I can't see a practical implementation. Could you elaborate a use case? Eg. a slider block. You'll need to provide a view implementation, at least, along with the block model. A complete different thing is that someone wants to create a complete different slider block view based on your base model, which is ok. Add-ons are complete by default, and have sensible defaults so integrators expect at least one reference implementation.
Correct in that you can model a block without how it is being display
. The difference between current Volto and this proposal is how your data is passed to that block. Taking the slider block example, you would write the slider component (this could be a series of nested components or even just a nunjucks macro, we're just talking about some level of isolation here) in your frontend of choice. The 'make it a Plone block' part of the story would come from how you integrate the data you get from the plone listener
into the component. The same code is used for view and edit mode, the only difference is that the data is coming from the edit mode listener
rather than view mode listener
There's still another dimension that I can think of. Block Styling in the block model. Did you think about it?
Again, we're completely impartial as to how you display anything here. Controlling styling through block or site settings would be done the same way as how it is done with @plone/components
, through CSS properties.
- because StoryBlok can do most of this then it should be possible.
We can't compare a VC-backed startup with the development power of the Plone community. We don't have the resources nor the moneys for that. It should be possible, but we have to keep in mind our limitations.
Agreed here. I've touched on this below, but we should try to find a way to build this that is maintainable as a community
Take Volto and turn it into something like StoryBlok. Either has a mode of current volto or as a fork. This would be a proof of concept only. If it works well then it could replace the current Volto editor weather your decide to use volto for your frontend or not.
Volto is Plone CMS reference implementation for the so called modern frontend technologies, and it's what it is. Volto follows the same monolithic approach that the ClassicUI approach, and we developed it in the image and likeness of ClassicUI. We cannot change it overnight, nor push for such a fundamental change, because lots of existing projects rely on it being like is it now. The Plone Community don't have the output nor the need for that. So I'd say it's out of the question. It has to be developed aside Volto, because conceptually it's another thing.
I'd agree that this is one of the biggest contention points: maintenance of such a technology as a community and I think needs further discussion as a community as to how to take this forward. Some people are already struggling to keep up with the big sweeping changes coming in future Volto, introducing an entirely separate technology may confuse integrators further. However, we could also build this headless editor idea in a way where the core components of it can also be used by current and new Volto, sharing the codebase and having things be more maintainable.
For example, we could make the plone listener
part of the headless editor something that is used by Volto Admin UI to update changes.
- Custom block types are created in the admin UI with a schema, similar to TTW content types editor. This is just defining the settings and storage for the block. Their frontend then takes the page with the block definitions and renders them using whatever kind of component they want.
Where would that model be saved? in the backend? What benefits would that bring? aside from allow the users create blocks on the fly. How the view part would adapt to a new field? How would it render it in the intended way? You'll have to adapt both sides still, right?
We've updated the PLIP to mark this as out-of-scope. I think it may be useful as part of the 'bigger picture', but isn't an essential deliverable for this project.
- When editing a page there is no point and click on blocks or inline editing. All changes are made in the sidebar and atter a change the iframe is reloaded so the preview is up to date.
As said before this is challenging, and not clear how this will happen in an agnostic (bring your own frontend) way.
Same as the above discussion about how the edit mode live listener will work.
- Only the restapi needs to implemented by the developer.
What needs to be implemented by the integrator then?
The intergrator's work will be taking the data from the plone listener
and working it into the props/ inputs for their 'component'. This will be both view and edit mode data.
Point and click, DND (overlay -> preview)
. Developer adds special block markup when edit mode event received via iframe bridge
- CMS puts layer over rendered iframe to intercept clicks and allows block selection etc.
- Now editor can click to select a block and get a quanta toolbar to manage block on the rendered page itself
- DND of blocks can now happen in the preview iframe
At first read, this seems to me from another world... an iframe, tracking scrolling, mouse events inside it forwarded outside? Maybe StoryBlock has gurus that are able to do this, no doubt about it...
StoryBlok gets you to assign a block UID (and additional information) to some HTML so that the bridge and visual editor can link information together. This would be part of the integration process. See https://github.com/storyblok/storyblok-js?tab=readme-ov-file#3-link-your-components-to-storyblok-visual-editor for how StoryBlok does it and how it provides helper functions to make this easier.
Inline Editing (preview -> sidebar)
The developer chooses to implement an additional api for some blocks which lets the user edit the content of the block directly in the preview page and change events are sent from the components to sidebar via the iframe bridge. On save this is send back via the restapi in the normal way. The editor might reusable component like slate or use completely different code.
This StoryBlock bridge, it's kind of magical, isn't it?
We aren't sure about the WYSIWYG story yet to be honest and is something we want to discuss further and would definitely love some community involvement!
We aren't sure about the WYSIWYG story yet to be honest and is something we want to discuss further and would definitely love some community involvement!
I've put in some more ideas on how this could work. If storyblok can do what it already does with overlaying block menus etc on blocks inside an iframe when all it needs is a data attribute to enable this. Then I think inline editing is also possible in a similar way. I've put more detail into the proposal how this could work in a way that requires very little code integration from the integrator. While I think that for many website projects, inline editing is not worth the effort, and you can already see this in many Volto plugins that decide to leave the editing to the side bar as its much less effort. On the other hand, if we can not lose this in the process of being decoupled, that would be a huge win.
I will take a look today!
We use something like this IFrame Bridge in our CMS Layer at e-ventis one Plone/Zope base. I would compare it to Level 3 of your PLIP.
If only serializable stuctures are used (like you are used to do it in REDUX), you are able to use buildin window.postMessage as gateway. This makes the IFrame nearly transparent. Send up events as JSON-like structure, send down changed data as requested from the server and re-render. Or sent down an action/command as JSON-like structure and trigger it inside the IFrame.
Could be a middleware/plugin in plone.client?
We also use something like RPC to call functions in a transparent way. Define a proxy-function inside the IFrame and run it outside in the editor or the other way round very easily.
I would love to give feedback on implementation detail or share our experiences, if it's wanted.
IFrame bridge is a really nice name, but it's no magic in my opinion.
https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage
https://en.m.wikipedia.org/wiki/JSON-RPC
@mtoepfl thanks for the added details. I also forgot to mention that the RPC part there are existing packages https://www.npmjs.com/package/iframe-bridge.
The way I've written it this IFrame bridge code is doing two things however and I think victor might have been referring to the how it will need to place visual elements like menus in the right spot overtop of the IFrame but not inside the IFrame. But given storyblok already does this, this is also not magic. The bridge code adds event listeners to clicks and mouse over to elements based on data attributes, sends a message across the bridge with an Id or location and a menu is placed at the right spot. Anything that is temporary like a menu can be done that way. Anything that has to scroll with the page like the block selection border might be better done inside the IFrame but these would have to minimal as the more visual elements you render inside the IFrame, the more likely it is that it could be broken by the integrators frontend code. Either way it's not magic.
@djay @JeffersonBledsoe I've taken a closer look at the updated info, I think that it looks a lot more better and it's more understandable, especially for newcomers and potential GSOC candidates.
From the GSOC point of view, I'd say that the only thing missing is to elaborate and clarify a bit more the deliverables. For example, I will focus in an specific implementation of the frontend to be demoed, and include it as a deliverable. I don't care what we use, but it should be used as a demo. Also, we will need detailed documentation, including the development/deployment story around it.
From the release manager perspective, I think we should stop saying that this will be part of Volto. We need to work still in the wording, which I plan to do in the upcoming sprints, but I really believe that Volto should remain the "reference implementation" of the monolithic approach and UX concept should remain the same. Everything that we develop from now on, should be aside it, as an addition to the Volto offering. Could it be used in conjunction with the new things incoming? most probably, as complement, we'll see. So Volto must continue being Volto for the time being. This is an important message to send to the community.
I am +1 to explore this way, since it seems that it could be useful for a number of use cases. I like the way you described the different architectural options. I still think that the "hybrid" and the "decoupled" have a lot of things in common (specially while you are on the simple side of the decoupled implementation), but we can polish the ideas.
I'll be honest with you though, the "decoupled" way that you depicted, specially reaching the L4, is the top tier in resources and energy concerns to be spent. I still see lots of small things that we haven't think of, like where to save the blocks schemas, deployments, development story... These and more, all will arise while developing. We've been there before, while we developed Volto, and the monolithic way, is the conceptually simplest one. I'm sure you are all aware of it, but I wanted to say it anyways. Getting the dynamics and the developer experience and deployment story won't be easy either.
I'm also concerned about the maintenance of the outcome of this after the GSOC. I'd like also that someone steps up and champion this. I'd like specially to be a company or companies to back the "whole" decoupled CMS UI idea.
I won't also hide my personal intentions, since I think that it's also fair for me to be transparent. While I'd like to the "decoupled" way to be a reality, I personally like more a concept where the development experience and the user experience get together into a solution that is half way the "decoupled" and the "hybrid" way. Of course, then you have to tie your development to one platform (at least), and be RESTAPI powered. But I still think that's down to earth, and could show people how to do it in other platforms (Angular, Svelte, Solid, Qwik, etc...). By the way, this concept must also be provided outside Volto, for the same reasons exposed above.
True, the decoupled way you barely will have to touch the CMS UI, but it will force you to write your adaptation in your frontend every single time, for every single block. This defeats the whole add-on concept. You won't be able to create an "slider" add-on that works for both sides. The add-ons architecture we created is one of Plone's community super-powers, both in classic and in Volto. Decoupling it from Volto was huge, and it's still to be seen its consequences as it opens a new world of possibilities.
Anyways, I don't want to engage here in a list of things that why I like more an in-between solution. I will work in drafting also such a solution so I put my thoughts also in words, again, not as an alternative but as a complement to the "decoupled" way. That's why I loved that you defined the different architectures in such a way.
Finally, thanks for pushing for this and volunteer for mentoring the GSOC project!
@sneridagh I have to admit I'm not 100% clear on everything you are saying but we can discuss it in the first Headless interest group meeting.
I'm glad the revised explanation now makes things more clear.
From the GSOC point of view, I'd say that the only thing missing is to elaborate and clarify a bit more the deliverables.
We will work on this
Also, we will need detailed documentation, including the development/deployment story around it.
You are right. The two things the POC is trying to do is show
- What is the api an integrator will have to deal with to make a custom frontend. Is it simple to understand and not time consuming. If the documentation is clear and others can quickly follow it and get something up then this is a success.
- What is the editor UX going to be like. How functional can we make it. How close to the Volto UX can we get.
From the release manager perspective, I think we should stop saying that this will be part of Volto. We need to work still in the wording, which I plan to do in the upcoming sprints, but I really believe that Volto should remain the "reference implementation" of the monolithic approach and UX concept should remain the same.
This is a fair point. If you say Volto is developer experience of combining a frontend framework and the Admin UX then you are right that a Decoupled developer experience needs a new name. (BTW Volto isn't monolith. still not sure I got the terms right. Monolith means all serverside. But Volto isn't headless which implies there is no expectation to build on a certain framework).
I didn't want to say Volto Headless since we seemed to be talking about slightly different things both being Headless. Plone Decoupled or Plone D? For me this would be a system that has a backend you don't have to change much. Has a Admin UI you can install and don't have to change much or at all. and has documentation on how to make your frontend easily. Any SDK or reusable components that help for specific frameworks would of course help. For me this is what a headless CMS means, not just that it is theoritcally possible to connect to the restapi and make your own frontend, ie Headless == Decoupled for me.
I'm also concerned about the maintenance of the outcome of this after the GSOC. I'd like also that someone steps up and champion this. I'd like specially to be a company or companies to back the "whole" decoupled CMS UI idea.
One way it could do is that it works well and it turns into a codebase we can promote. The really nice this is that once the Admin UX is decoupled it can be rebuilt using all the nice reusable components and more modern techniques with much less concern for breaking backwards compatibility. This should result in being able to make the editor UX better faster.
Another outcome is that it doesn't work at all. The work an integrator has to do is too much and the editor UX is flaky in ways that were aren't good solutions to. In this case then Volto but made cleaner and more remixable is perhaps the best option.
The most likely outcome is that it kind of works but needs a lot more work. But we will have a much better idea of that work is feasable or not I hope.
If it does work well. Then there could be a way where compatibility with volto is possible, or at least not so jarring an upgrade. With Volto as it is now the customisation is mostly overriding a Block render view to make it look different. Volto as a frontend React framework that allows for overriding the builtin blocks could still exist. It's just that we would need to seperate overrides to the Admin UI and overrides to frontend as these would be seperate codebases. So code would have to be changed because of that. Also how the Volto frotend talks to Admin UI would of course have to go via the iframe bridge and not within react states and anyone who did more complex integrations assuming that would have to change their code for that reason also.
I won't also hide my personal intentions, since I think that it's also fair for me to be transparent. While I'd like to the "decoupled" way to be a reality, I personally like more a concept where the development experience and the user experience get together into a solution that is half way the "decoupled" and the "hybrid" way.
It's true it will always give you more flexibility and more freedom. But I also think we don't have to give up on customising the Admin UI. This is our advantage. Being open source we can allow developers do customise the admin UI using code where SaaS CMS can't.
True, the decoupled way you barely will have to touch the CMS UI, but it will force you to write your adaptation in your frontend every single time, for every single block. This defeats the whole add-on concept. You won't be able to create an "slider" add-on that works for both sides. The add-ons architecture we created is one of Plone's community super-powers, both in classic and in Volto.
Maybe a slider addon doesn't make as much sense in a decoupled headless since the Admin UI is really just a container that contains teaser like blocks and that should be something generic we get out of the box. But if you consider something like form-block. This is still a useful addon. It requires more complex Admin UI and it needs backend code and its own api. It could come with its an example Volto version of the field blocks so if it fits to use Volto then its easier. But also the integration experience with a custom frontend is perhaps not as bad as it sounds. You will have versions of input fields and date fields and error messages in whatever framework or DS you are using. You just have to wire then up to take defaults, do validation etc based on teh addon api. Then some iframe bridge integration it want those to be a live preview while editing. The addon still saves you a bunch of time.
What the decouple editor with an iframe bridge is doing is putting a very defined api between the view component and the sidebar edit UI (and the block selection, DND etc). Which is a good thing as this is all blured right now as you know. And that creates upgrade issues for anyone building on top of volto now. I know you are trying to solve how far apart to keep the editing UI vs the view/render component. The answer is far :)