Structured editing and tighter integration with CKE5


(Dmitri Pisarev) #1

The problem: ever tried to insert an image in the middle of a text node? Yes, split it in two, then insert an image in between. All editors hate it and always complain how easy it is e.g. in wordpress.

The possible solutions:

  1. How magnolia does it. Let each paragraph be a separate node. Create new paragraph node on hitting enter, delete a node when hitting backspace on empty editable.
    Pros: rather easy to implement poorly. Structured.
    Cons: rather hard to implement well, as we are basically taking over most responsibilities from CKE. Not so good UX when you need to handle the whole text, e.g. you can’t just select a range of paragraphs and apply some style to it, or delete them (unless we take care of it).

  1. Create a special editing annotation for a ContentCollection-like nodes, that would take CKE5’s internal model, and map all elements to separate nodetypes. Then map other standard Neos nodetypes as custom model elements rendered as CKE5 widgets, e.g. an image, that would just be in between those paragraphs.
    Pros: Structured. Really good transparent UX, would feel just like a native CKE integration. E.g. you select a paragraph, convert it into headline, and behind the scenes it’s automatically converted into a Headline nodetype.
    Const: tricky to implement right, a lot of work. Would be a hard investment into CKE5.

  2. Hackish naive solution. Just add one more CKE toolbar item that would trigger a standard add node dialog, split a text node into two at the cursor location and insert a new node in between.
    Pros: easy to implement, kind does the job.
    Const: data remains mostly unstructured, e.g. headlines and blockquote elements would either remain unstructured or would still need a lot of clicks to create.

I personally love option 2, but don’t know if we can afford it.
Thoughts?


Team Minions | Team Meeting | 2018-07-31
(Christian Müller) #2

If you want to split you basically want the same NodeType (again) right, so couldn’t a simple solution be a “split here” button that will take everything from current cursor position to end and put it in one new node of the same type?


(Dmitri Pisarev) #3

Exactly. Could be two buttons, split here and insert here, just to save a few precious clicks.

edit: maybe not just the same type, but actually a clone of a given node, maintaining all other properrties but the current text field.


(Sebastian Kurfuerst) #4

Hey,

I have a 4th idea (would describe it in detail this evening or tomorrow):

We allow to add a Node inline in the text, with a special marker where the subnode is placed in the text.
So that would allow to place some node types as inline nodes (e.g. CKEditor Blocks)

That is IMHO combinable with the “split” feature.

All the best :slight_smile: Sebastian


(Sebastian Kurfuerst) #5

Hey Dmitri,

great that you are putting thought into this definitely exciting topic :slight_smile:

tl;dr: I’d go for variant 3 plus the fourth option, outlined below.

Going Meta

Before delving into the topic, I’d first like to discuss about Content and Document nodes on a “Meta” level:

To me, besides all the technical detail differences (e.g. Document has a URL, Content has not), there is one main “philosophical” difference between the two for me: Document nodes make sense on their own, e.g. they represent an own distict entity which has an external identity, usually has a meaningful external identifier (=name), and a Document (=a Document Node and corresponding content) is a unit of sense. That means you usually can look at a Document without knowing much context, and you will understand a certain information.

On the contrary, Content is a different beast: It is part of a larger Document, and it conveys a certain “information snippet” (like a sentence, a paragraph, an image, or a longer text). However, a single Content node on its own, e.g. without its surrounding Content, is only conveying a smaller snippet of information. That is, to make sense of it, you often need surrounding context (=the nodes before and after).

Of course, this is not meant black-or-white: Some parts of Content, especially those types containing nested-content, often convey meaningful information on its own. The text above basically describes the ends of the spectrum to me.

Consequences of this “Meta” difference between Documents and Content

Because of the differences outlined above, the following behavior is adjusted in Neos for Document and Content, respectively:

  • the unit the editor sees at once is a single Document (usually)
  • Documents have a distinct URI, Content has not.
    • -> You can link to Documents, but not to Content
  • (Content Dimensions) When translating, you usually do it on a document-by-document basis.
    • -> for Documents we have a very rigid connection between the different variants of a Document, i.e. you want to link to the other-language variants of a page.
    • -> we move the different variants of a Document node in all dimensions at once, because we believe the entity should always be locatable at the same position across all dimensions. (sidenote: this will be made more flexible lateron; but I believe still it is a useful default)
    • -> for Content, you can more freely restructure it; you might even sometimes have no 1:1 mapping of content on different dimensions. Example: For some RTL-languages, you might want to place content in different containers, in a different ordering; or you might want to not show certain images in a certain language.
    • -> basically, it’s the editor’s responsibility to convey the same information on a Document in different dimensions; by using any Content nodes he wants/needs.

Why do we need Content nodes at all?

The above statements might sound like for Content, it would be enough to store everything in a large “Blob” (e.g. how Gutenberg/Wordpress does it). It turns out this is not the case, because:

  • we can use constraints to restrict which kinds of content are allowed at which position
  • often, there exist some “grouping” content nodes, like stages, semantic sections, highlight blocks, … - which convey a content block which is sufficiently able to stand on its own.

-> so by having (tree-structured) Content nodes as well, we allow for a big amount of flexibility to allow to cater to all kinds of use cases.

Back to CKEditor integration

Now, mapping the argumentation above to the 3 ways proposed by @dimaip, they score as following for me:

  1. Rather “un-pragmatic”. We are enforcing lots of “content structure” without any semantical meaning. (that’s why I put content structure in quotes: It looks as if the content is structured; but it does not convey semantical meaning)
  2. similar drawback as 1: it looks structured, but in fact it’s just a mapping of the paragraphs and headlines we have (at the lowest level). We do not really have semantical meaning here IMHO.
  3. this solution assumes that always where you need a special content (e.g. insert an image, …), something semantically relevant takes place. This assumption is sometimes right, sometimes wrong I think. In many cases, this is a good indicator for some semantic “belonging”, but sometimes it is just done for visual/aesthetic reasons. Because of this, for me this is the best solution of the proposed 3 solutions, because it balances pragmatism and well-structuredness.

Besides mapping the “philosophical argumentation” to the 3 proposed solutions, I see the following further properties:

  1. We would loose a common undo/redo stack inside a bigger chunk of text - and people are accustomed to it. Same for Copy/Paste of longer texts, which would be way harder.
  2. As the rendering of Nodes is controlled by Fusion, it would be kinda hard to map this to the CKEditor world - essentially, CKEditor would need to know how a certain Node is rendered at a certain location by Fusion. However, this is totally hard because of the flexibility of Fusion (conditional renderings, …)

Further Proposal: Inline Nodes in Text-Properties

To me, the main problem with solution 3. is that purely for getting a certain aesthetic, I am forced to use a certain Node structure. Further, creating things like e.g. inline images, or having a long text with references to other entities (which are not plain links, but more “widget-like”), gets more and more complicated. Examples could be displaying Stock Market indices next to a company name; or birthdates next to person names.

So that’s what I would want to solve, by combining how we currently do inline links with nested Nodes:

  • In an inline editable text block, one could insert a nested Node inline at the cursor position. The system would insert markup like .
  • At rendering time, the markup would be converted to a rendering of the specified Node UUID.
  • These nodes are child nodes of the current Node; they are created only via CKEditor itself and purely managed in CKEditor. They are not creatable outside of CKEditor. When you remove them in CKEditor, they must be deleted behind the scenes.
  • These nodes always exist in the same Dimension as its parent node (e.g. the node where they belong to).
  • In CKEditor, they could be represented by what I believe is called Blocks; i.e. non-editable content areas.

Closing thoughts

This has been a rather long post now :sunny: - Hope you don’t mind and I was able to somehow express my thoughts. Looking forward to the discussion :heart:

All the best,
Sebastian


(Martin Ficzel) #6

I agree to @sebastian that currently content is mostly poorly structured wich results in markup structures like h2, h4,p,p,img,h3,p,h3 that hardly make any sense. Basically a list of blocks without much further meaning. However this is how html is specified and also how common text editors handle structure of texts. I doubt we can be better than our target platform structure wise, even if we find a solution for that i doubt that it will feel natural to editors.

I fear the solutions one and two that @dimaip suggested are really hard to implement. Especially option two will get into conflict with fusion-rendering quite a lot. That is why i suggest to start with solution three, gain experience and improve on that.

Since the split-collapse feature is nothing we expect from every node this could be isolated into special “autospliting-text-block”-nodetype (mixins), so the whole splitting and reconcatenating would “only” have to deal with consecutive lists of such nodetypes. For convenience a node-template for the split-nodes could avoid the create dialog.


(Dmitri Pisarev) #7

First of all, thanks for the feedback on the topic, something to think about.

Why is that a problem? I love nodes, I know how to use them… I treat nodes/nodetypes as a sort of “virtual DOM”, a data abstraction that allows us to attach semantical meaning to the data when there is such meaning and to tighter control the rendering, querying and aggregating the data as opposed to a blob of HTML that you see in most other CMSs. It’s a base building block that you can use according to the task, so if you have just a blog, I feel like it’s perfectly fine just to have Text + Headline + Blockquote + Image + Grid nodetypes, they describe as much semantics as there are in this piece of content, and if this content is not deeper structured and requires just aesthetics, then so be it.

Anyways, I’m no good at having such theoretical discussions in written form, so if we’d find ourselves in strong disagreement here, I’d love to move that discussion to a Hangout.

Not to get too theoretical, I’ll try to write some user stories:

A) As editor I want to be able to quickly insert other nodes in between paragraphs
(Visually it could look like that you hover a paragraph, and next to it you see a ‘+’ button. Then it does what is described in solution 3.)

B) As integrator I want to have more data represented as nodes, e.g. heaving headlines as a separate nodetype allows to build up ToC, customize rendering depending on context, insert anchor links etc.

C) To achieve B), as editor it must be really easy to select a paragraph and convert it into a separate headline, blockquote etc. Then it would feel like a combination of solutions 1 and 3.

So yes, I think we should start with solution 3 and see how far we’d get away with it.


(Christian Müller) #8

Just a short generic answer:

In an ideal world we would have (independent) content parts that are semantically wrapped with article or section (or similar) tags and get nested downwards in specificity. Or at least that’s how I would like to see it.
To coerce this structure from editors you would probably simplify and say headlines define the beginning of such a structural part and then have the content that is associated with this headline nested below the headline (node wise). Which would result in a semantic structure roughly like this:

<!-- From headline component -->
<section>
    <h3>About Neos</h3>
    <!-- Text element nested IN headline -->
    p>Foo bar Neos</p>
    <!-- end text element -->
    <!-- nested headline in the upper one, creates a separate "group" again -->
    <section>
        <h4>The Neos Teams</h4>
        <!-- text element nested in the (sub)headline -->
        <p>The Teams foo bar</p>
        <!-- end text element -->
    </section>
    <!-- end nested headline -->
</section>
<!-- end headline -->

(Dmitri Pisarev) #9

I think it would be great to have a hangout about this (maybe next week?), because for me it’s a bit hard to understand what you guys are suggesting and why. Would be easier to just have a live conversation.


(Sebastian Kurfuerst) #10

Hey,

@dimaip I am totally fine with variant 3; would be definitely my favorite which you described.

Next month I am offline travelling; so I won’t be able to join a Hangout :slight_smile:

All the best, Sebastian


(Dmitri Pisarev) #11

Hey, I think there’s no rush, I don’t plan to be working on the topic real soon. I just really want to grasp your ideas guys, and I feel I fail to do that on the written medium.

Gute reise!


(Dmitri Pisarev) #12

Meanwhile I did a quick implementation of solution 3): https://github.com/psmb/Psmb.SplitAdd

It does the job, but of course it feels a bit hacked on, e.g. there’s now one more plus button, in addition to the standard one, and you definitely need to explain the concept to editors, i.e. it’s not intuitive at all.

I looked closer at http://testgutenberg.com and it seems they are also going the solution 1) way:
every paragraph is a separate element, on it is created, on backspace empty paragraphs are removed, they are even selectable together, you can quickly convert paragraph element to headline or blockquote. In other words they kind of emulated the text editor behavior, but across multiple text editors:

Our main impediment currently is that we as CMS developers don’t have any knowledge about roles of nodetypes that we are dealing with. E.g. if we knew that Neos.Neos.NodeTypes:Image is a default node for representing images on a particular installation, we could add a lot of fine-tuned behavior for it, e.g. automatically creating new image nodes on paste from clipboard etc.
Maybe we should come up with a definition of a few functional UI roles that would get allow a special treatment in the UI?
E.g. image, text, headline, grid.

Anyways, just a quick brain dump, needs more thought.


(Ernesto Baschny) #13

Just a quick intervention.

Please do not “split by paragraph or headlines while typing” in CKeditor, as we would loose lots of “structure” that we learned to love while handcrafting our content elements. A content element is not only “text”, but more complex entities, and usually also have other “meta data” attached to it (i.e. layout, start/enddate, etc). Sometimes a bunch of paragraphs needs to have the same meta-data applied to it as a group.

So in my opinion having a possibility to “split content elements” on demand is a pragmatical but very good choice already for the most common use-cases. “Nesting content elements” (being able to add content elements inside text blocks, @sebastian idea) sounds interesting, but I can imagine it to become cumbersome to work with (in the UI, in the CR, regarding caching, workspaces etc).