Skip to content

Conversation

callumbwhyte
Copy link
Contributor

@callumbwhyte callumbwhyte commented May 21, 2019

Final commit period has been applied, this RFC will end 29/07/2019


See RFC document in this PR proteus-block-editor.md

Let's start discussing how we can improve data storage for complex editors (e.g. Grid/ DocType Grid Editor (DTGE) / LeBlender Editor, Nested / Stacked / Inner Content editors etc)!

@callumbwhyte callumbwhyte changed the title Initial proposal for new block-based editor: Project Proteus New block-based editor: Project Proteus May 21, 2019
@Shazwazza Shazwazza changed the title New block-based editor: Project Proteus RFC 0011: New block-based editor: Project Proteus May 21, 2019
@Migaroez
Copy link

The rfc states :

In the future blocks could store a reference to a published document (IPublishedElement) as a UDI, rather than embedded JSON objects. This unlocks the possibility for variant blocks, segmentation ...

By segmentation do you mean variations not tied to culture, like being able to change settings/content based on viewport breakpoints. This question arose during one of the open circle sessions at cg19 relating to get a css grid based grid into umbraco. If so, there is a need to expose an api endpoint to request a certain block (by id?) with a current culture (for variants) and a certain variation. So that viewport based variations can be loaded async.

@callumbwhyte
Copy link
Contributor Author

Hey @Migaroez!

Thanks for engaging with our RFC 👍

This RFC is specifically about the data structure - extending features of IPublishedElement (e.g. variants) and storing references to documents is currently listed as out of scope for this discussion. There will be a future RFC to discuss how this is implemented.

However your point about there being "a need to expose an api endpoint to request a certain block... so that viewport based variations can be loaded async" sounds potentially interesting... As I wasn't in this discussion can you clarify what you mean? What's the use case for this? Does this need work for all possible implementations of the block editor, or is this CSS-Grid specific?

@kjac
Copy link
Contributor

kjac commented May 27, 2019

Take-aways from the open session at CodeGarden

Proteus was discussed on in an open session at CodeGarden. Here are the take-aways from that session.

Questions and answers

How to support non-list/grid use cases like maps?

This will be supported right out of the box; the RFC already mentions this.

How to store additional data on the property editor, e.g. map zoom level?

Could this be achieved as a custom property editor with blocks nested inside it? Something definitively worth prototyping while implementing.

Add sufficient lower level abstractions for reuse, e.g. if the particular use case does not conform to a list of things.

This has already been started out with a directive approach. This is definitively something for the implementers to keep in mind.

Support for custom/overridable property value converter?

This should work right out of the box if you create a new property editor using blocks.

How to tackle position data within the data model (row x, col y)?

Definitely worth a think for those still wanting to use bootstrap style grid implementations. Perhaps a PoC could shed some more light on this.

Must ship with default use case implementations.

Yes. Just need to figure out which (list, css grid, bootstrap grid, ...?). See also #12.

How to copy content between editors?

Note to the implementers to support the clipboard service at an applicable level (probably when adding a new block).

Must be possible to extend quickly.

Creating the default use case implementations should take care of this, providing meaningful abstractions for that purpose.

How to support macro blocks in the editor?

That’s a really good question. How do we execute things on the server when rendering the structured data. And should we even do that?

Other relevant input

When implementing the grid in V7, previews of editors are mostly implemented as vague-ish representations of what the data will look like on the frontend - not as pixel perfect previews.

The default grid config/setup is not really used; people mostly use DGTE + LeBlender.

@nielslyngsoe
Copy link
Member

Hi Everyone

Thanks for your inputs, Can I ask if you have any concerns regarding the proposed data structure for storing blocks?

Thanks

leekelleher and others added 2 commits June 5, 2019 08:01
@leekelleher
Copy link
Member

Before I respond to the Unresolved Issues on this RFC, I feel that #11 and #12 are currently mistitled.

Am I correct in thinking that #12 is about the editor itself and #11 is the data structure (for interoperability purposes)?

In that case, is it worth retitling them to...?

  • RFC 0011: Project Proteus: Block-based data structure interoperability
  • RFC 0012: Project Proteus: New block-based editor

In response to the questions in the Unresolved Issues section...

Is this the best data structure for storing blocks?

I like the format of separating content from config/settings.

Maybe "settings" could be an IPublishedElement too? In essence, they are name-value pairs, (with extra meta-data about the type itself).

I wonder if "settings" is the right term, maybe "configuration"? I appreciate that's my personal preference.

In terms of the general structure of the JSON, I'm not sure about the container items, whether they'd be a single-dimension or multi-dimensional arrays? (As @kjac mentioned above How to tackle position data within the data model (row x, col y)?)

Once the element is sharable, how do we manage the data manipulation (Edit, remove) between the different implemented block-based editors (e.g. Nested Content to Grid)

We may need to clarify this question. Are we taking about the short term (JSON) or long term (UDI)?

Both are leading towards block-editor interoperability (which is a great idea! 👏)

As for how practical it would be to swap between Grid and Nested Content is debatable. Again coming back to the differences in the container items, e.g. going from a multi-column to a single-column - how should the blocks be preserved?

Are we also saying that it should be possible to swap a Nested Content (say that had latitude/longitude textstrings) with a maps editor (and vice-versa)? I guess my question is how far do we want to take the interoperability?

How can we best handle validation while editing blocks content, given the many forms block editors can take (e.g. infinite editors, inline, etc)?

  • How do we handle required fields?
  • How do we handle custom field validation (e.g. via RegEx)?

From my own experience of attempting to deal with validation of nested element editors, it quickly became a UX nightmare. Mostly due to knowing when validation should occur, then how to display indicators/notifications.

Given the complexity of some editors, server-side validation might be required at every nested level. But then there's the issue of "should you be able to save invalid (or un-entered) content?" e.g. a work-in-progress?

Maybe proposing a pattern of validating nested elements sites outside the scope of this RFC?


Quick note about the longer term - UDI approach. There have been various discussions around having a "-2 repository", (I'd discussed this concept with @zpqrtbnk at CG17 Retreat and @pgregorynz suggested this at CG19's Dream Corner), whereby we'd have...

  • -1 for routed content
  • -2 for non-routed content

@Shazwazza
Copy link
Contributor

@leekelleher Quick note on the "UDI approach", part of the out of scope topics for this RFC is changing the storage mechanism, see https://github.com/umbraco/rfcs/blob/731e1872323a2e3a8c4cd8c91524c795f3c5efa3/cms/0000-proteus-block-editor.md#out-of-scope (i.e. Creating a mechanism to create and maintain PublishedElements as distinct documents in the database). If/When we decide to do something like that we will need to overhaul how documents are stored in the database because currently with relation tables to do this work it is very expensive to read and to write. If a single content item had several block property types and each editor contained 20+ items and if these items were all stored as DB content the way we currently have it, you can imagine how many DB queries this would take to render the editor, and the imaging trying to save all of this information again ;) AFAIK i think the intent of this RFC is to come up with a standard way of representing a 'block' which is really an IPublishedContent along with a 'config'. Eventually we should/could be able to then move this standardized data into something like a real content item if/when we overhaul how the DB stores content items (which will need to be like a document storage).

@callumbwhyte with regards to this RFC and what is in/out of scope i think the RFC might need adjusting since it's a little confusing. We say that long term goals are "Make the element shareable, stored as elements in database." but then say this is out of scope: "Creating a mechanism to create and maintain PublishedElements as distinct documents in the database" but then go on to ask the question "Once the element is sharable, how do we manage the data manipulation (Edit, remove) between the different implemented block-based editors (e.g. Nested Content to Grid)". IMO that question shouldn't be asked here should it? Since this RFC isn't proposing having 'shared' elements yet and storing them separately in the DB


... and on that note, we already have a standardized way of storing a content item in JSON, we are already doing this in the cmsContentNu table which is storing a JSON document for a content item. I think potentially we should look at just using this structure to store the block data since it will be based on a doc type and will essentially be content. Then when/if we overhaul how the DB stores content, we will just use this same approach for document driven content storage. Of course the 'config' part of the storage will exist outside of that json block.


Regarding validation,

For server side validation: We do have a mechanism for this to work already which is based on keys that are defined in the angular markup and the server will return errors for those keys (via ModelState) if there are any. Things get a little complicated when we have sub editors like nested content, etc... (for which we don't currently have server side validation wired up! but we should do and could use this design) and that's because we haven't made a standardized way of associating the keys but this could 'just' use the index of each item. So currently if a property type has server side validation problems, the key would be (for example if it's an invariant property and the property type alias is title): _Properties.title.invariant . In this example, this is an error for the entire property, not for a specific field of the property. If we want to target a specific html field (like an html textbox that has an input name of myTextBox) in this property we can do that, the key would then be _Properties.title.invariant.myTextBox

We can iterate on this for sub content. So for example, if a block with an index of 2 has a text area property type that has a server side error (i.e. the regex validation fails), the 'key' to associate it could be _Properties.myBlocks.invariant.[2] which would indicate that the whole block has an error. If we wanted to then target a specific html field, it could be _Properties.myBlocks.invariant.[2].myTextBox

I think this type of key handling could work in scenarios with any amount of nesting, like maybe you have a nested content item inside of a block: _Properties.myBlocks.invariant.[2].myNestedContent.[1].myTextBox (I'm not actually sure if there are cases where this doesn't work)

@Shazwazza
Copy link
Contributor

I think Lee is correct here

In that case, is it worth retitling them to...?
RFC 0011: Project Proteus: Block-based data structure interoperability
RFC 0012: Project Proteus: New block-based editor

@callumbwhyte do you agree?

@callumbwhyte callumbwhyte changed the title RFC 0011: New block-based editor: Project Proteus RFC 0011 - Project Proteus: Block-based data structure interoperability Jun 21, 2019
@callumbwhyte
Copy link
Contributor Author

Agreed! Name updated

@Shazwazza
Copy link
Contributor

Hi @callumbwhyte , not sure if you had a chance to read my long comment above #11 (comment) where I've mentioned you ?

I think the 2nd question in the Unresolved Issues section shouldn't exist in this RFC

Once the element is sharable, how do we manage the data manipulation (Edit, remove) between the different implemented block-based editors (e.g. Nested Content to Grid)

This isn't something we will be able to answer now or until it's "shareable" which also means it will be stored in the DB, which is out of the scope of this RFC .

I also think that the 3rd point there about 'validation' is probably out of the scope of this RFC, this RFC should really just be down to how data is stored. Validation is an implementation detail and will probably require some prototyping. I think that this should be either listed as out of scope too or simply removed.

@Shazwazza
Copy link
Contributor

For JSON storage of blocks - which is really what this particular RFC is about - we want to consider that: Each 'block' should be IPublishedElement (which is a subset of IPublishedContent)

As it turns out, we already serialize content items (IPublishedContent) as JSON in the database that I've mentioned above:

... and on that note, we already have a standardized way of storing a content item in JSON, we are already doing this in the cmsContentNu table which is storing a JSON document for a content item. I think potentially we should look at just using this structure to store the block data since it will be based on a doc type and will essentially be content. Then when/if we overhaul how the DB stores content, we will just use this same approach for document driven content storage. Of course the 'config' part of the storage will exist outside of that json block.

As you'll see below the format of this is fairly simple, but some things like url segments, culture, segment will be unneeded. This format also supports variant content but i didn't post this example below since blocks for now and for this RFC will be "Invariant". @callumbwhyte perhaps the RFC should be more clear about this?

As for the "Settings" or "Configuration" portion of a block editor. Having this as name/value pairs is fine, this just means that it totally open-ended by the Settings/Config editor that needs it since the value part can be any JSON object. This is all ok, but pretty much means we don't need to define a format for this, it's just a collection of "name": [object]. The main question then is where does this fit into the bigger json structure? Suggested options are: "configuration", "settings", "config"

Here are examples of this format:

Media

{
	"properties": {
		"umbracoFile": [{
				"culture": "",
				"seg": "",
				"val": "{\"src\":\"/media/662af6ca411a4c93a6c722c4845698e7/00000006000000000000000000000000/16403439029_f500be349b_o.jpg\",\"crops\":[{\"alias\":\"banner\",\"width\":1000,\"height\":300},{\"alias\":\"thumb\",\"width\":30,\"height\":30},{\"alias\":\"square\",\"width\":300,\"height\":300},{\"alias\":\"asdf\",\"width\":456,\"height\":789}]}"
			}
		],
		"umbracoWidth": [{
				"culture": "",
				"seg": "",
				"val": 1600
			}
		],
		"umbracoHeight": [{
				"culture": "",
				"seg": "",
				"val": 1067
			}
		],
		"umbracoBytes": [{
				"culture": "",
				"seg": "",
				"val": "759116"
			}
		],
		"umbracoExtension": [{
				"culture": "",
				"seg": "",
				"val": "jpg"
			}
		]
	},
	"cultureData": {},
	"urlSegment": "umbraco-campari-meeting-room"
}

Invariant Content

{
	"properties": {
		"heroHeader": [{
				"culture": "",
				"seg": "",
				"val": "Umbraco Demo"
			}
		],
		"heroDescription": [{
				"culture": "",
				"seg": "",
				"val": "Moonfish, steelhead, lamprey southern flounder tadpole fish sculpin bigeye, blue-redstripe danio collared dogfish. Smalleye squaretail goldfish arowana butterflyfish pipefish wolf-herring jewel tetra, shiner; gibberfish red velvetfish. Thornyhead yellowfin pike threadsail ayu cutlassfish."
			}
		],
		"heroCTACaption": [{
				"culture": "",
				"seg": "",
				"val": "Check our products"
			}
		],
		"HeroCtalink": [{
				"culture": "",
				"seg": "",
				"val": "umb://document/ec4aafcc0c254f25a8fe705bfae1d324"
			}
		],
		"test2": [],
		"bodyText": [{
				"culture": "",
				"seg": "",
				"val": "{\r\n  \"name\": \"1 column layout\",\r\n  \"sections\": [\r\n    {\r\n      \"grid\": 12,\r\n      \"rows\": [\r\n        {\r\n          \"name\": \"Full Width\",\r\n          \"areas\": [\r\n            {\r\n              \"grid\": 12,\r\n              \"allowAll\": false,\r\n              \"allowed\": [\r\n                \"media\",\r\n                \"macro\",\r\n                \"embed\",\r\n                \"headline\"\r\n              ],\r\n              \"hasConfig\": false,\r\n              \"controls\": [\r\n                {\r\n                  \"value\": {\r\n                    \"macroAlias\": \"latestBlogposts\",\r\n                    \"macroParamsDictionary\": {\r\n                      \"numberOfPosts\": \"3\",\r\n                      \"startNodeId\": \"umb://document/1d770f10d1ca4a269d68071e2c9f7ac1\"\r\n                    }\r\n                  },\r\n                  \"editor\": {\r\n                    \"name\": \"Macro\",\r\n                    \"alias\": \"macro\",\r\n                    \"view\": \"macro\",\r\n                    \"render\": null,\r\n                    \"icon\": \"icon-settings-alt\",\r\n                    \"config\": {}\r\n                  },\r\n                  \"active\": false\r\n                }\r\n              ]\r\n            }\r\n          ],\r\n          \"hasConfig\": false,\r\n          \"id\": \"314cb47b-fbe1-eeb6-f7cc-38db875d0f06\"\r\n        }\r\n      ]\r\n    }\r\n  ]\r\n}"
			}
		],
		"footerHeader": [{
				"culture": "",
				"seg": "",
				"val": "Umbraco Demo"
			}
		],
		"footerDescription": [{
				"culture": "",
				"seg": "",
				"val": "Curabitur arcu erat, accumsan id imperdiet et, porttitor at sem. Curabitur arcu erat, accumsan id imperdiet et, porttitor at sem. Vivamus suscipit tortor eget felis porttitor volutpat"
			}
		],
		"footerCTACaption": [{
				"culture": "",
				"seg": "",
				"val": "Read All on the Blog"
			}
		],
		"FooterCtalink": [{
				"culture": "",
				"seg": "",
				"val": "umb://document/1d770f10d1ca4a269d68071e2c9f7ac1"
			}
		],
		"footerAddress": [{
				"culture": "",
				"seg": "",
				"val": "Umbraco HQ - Unicorn Square - Haubergsvej 1 - 5000 Odense C - Denmark - +45 70 26 11 62"
			}
		],
		"HeroBackgroundImage": [{
				"culture": "",
				"seg": "",
				"val": "umb://media/662af6ca411a4c93a6c722c4845698e7"
			}
		],
		"font": [{
				"culture": "",
				"seg": "",
				"val": "serif"
			}
		],
		"colorTheme": [{
				"culture": "",
				"seg": "",
				"val": "earth"
			}
		],
		"sitename": [{
				"culture": "",
				"seg": "",
				"val": "Umbraco Sample Site"
			}
		],
		"SiteLogo": []
	},
	"cultureData": {},
	"urlSegment": "home-1"
}

@Shazwazza
Copy link
Contributor

Another thing that should be included in this RFC is how this data is actually typed. The end result will probably be something like?

interface IBlockData
{
    IPublishedElement Content { get; }
    IDictionary<string, object> Settings { get; }
}

@kjac
Copy link
Contributor

kjac commented Jun 24, 2019

I also think that the 3rd point there about 'validation' is probably out of the scope of this RFC, this RFC should really just be down to how data is stored. Validation is an implementation detail and will probably require some prototyping. I think that this should be either listed as out of scope too or simply removed.

The way I see it, Proteus should be scoped to provide both the data foundation and the interface abstractions needed to create a multitude of editors based upon it. As validation has historically been a real pain for block based editors in Umbraco, it would be really nice if Proteus could at least supply a means to handle validation on the clientside.

Another thing that should be included in this RFC is how this data is actually typed. The end result will probably be something like?

interface IBlockData
{
    IPublishedElement Content { get; }
    IDictionary<string, object> Settings { get; }
}

Ideally we would be able to generate strongly typed models on top of the individual block configurations. This would be a huge benefit for both server side and SPA style rendering of Proteus based data. But I believe that would increase the complexity of Proteus by a significant factor. Also it could be a future improvement; as long as whatever strongly typed model introduced would implement IDictionary<string, object>, it would still be backwards compatible.

@Shazwazza
Copy link
Contributor

@kjac As far as "Proteus" goes, this is a higher level feature/implementation that is so far made up of 2x RFCs but potentially could be more as we move forward. This RFC is specifically about the data structures that is stored for a block. I agree that of course validation needs to work and be consistent for these types of editors but I think we can make that work with what we already have with some tweaking. I've mentioned some of this above: #11 (comment). If necessary another RFC could be created to detail this but I think this specific RFC doesn't need to consider the details of validation and instead should just be specific to data storage. What do you think?

I agree that we should allow for strongly typed settings/config data, probably even strongly typed IPublishedElement data but this will have to be facilitated one way or another by casting and/or generics such as (which could possibly inherit from the non-generic class)

interface IBlockData<TContent, TConfig>
    where TContent : IPublishedElement
    where TConfig : class
{
    TContent Content { get; }
    IDictionary<string, TConfig> Settings { get; }
}

@leekelleher
Copy link
Member

Is there any reason or concern why the config/settings couldn't also be an IPublishedElement, (as in to be associated with a Content Type)?

interface IBlockData<TContent, TSettings>
    where TContent : IPublishedElement
    where TSettings : IPublishedElement
{
    TContent Content { get; }
    TSettings Settings { get; }
}

Which could then be strongly-typed - a la ModelsBuilder - on the front-end?

@Shazwazza
Copy link
Contributor

@leekelleher I think you're on to something there :) like you say this would have to mean that "configuration" for a block is also based on a Element Type ... which is probably a good thing. The "configuration" part of this RFC needs to be a little more formalized but I think it would make sense that it is also based on an Element Type, then we're just using the same experience for everything which i think makes perfect sense!

Does anyone have opinions on this?

@nathanwoulfe
Copy link

I think that's the friendliest way to manage settings/config for less technically inclined people - means there can be a low/no code method for managing configuration. Also means (eventually) configuration elements could be shared between different block types, could use composition to set base properties, data is typed, property converters work, all good stuff.

@Shazwazza
Copy link
Contributor

I would like to start wrapping up this RFC. To do that I need access to edit it from @callumbwhyte (have sent a msg).

I think the approach of storing a serialized version of IPublishedElement for both the content block and it's configuration/settings is correct and the serialized format can be very simple.

The end result is the serialized version of IEnumerable<IBlockData<TContent, TSettings>>

There was a question about "How to support macro blocks in the editor?" - I think this is relatively easy and as far as this RFC is concerned it's really about how the data is stored, not how the editor(s) will render them. The RTE and Grid has it's own functionality which can be re-used, we already have the capability to render a macro server side and display it, we can probably make this more consistent. For this RFC though, it's really about how to store a macro block and that is really just a minimal json serialized format of a macro. The same data that is stored in the RTE block just in json format. We can easily then make a nice directive to accept this JSON structure and render the block.

There was another question about "How to tackle position data within the data model (row x, col y)?" - this so far is largely not answered. For editors like Nested Content and the block editor proposal (#12), the data is just stored in an array. For editors like the Grid (not sure about Stacked Content, etc...) these are stored in their own proprietary way. So would it make sense to 'just' ... in the future these editors would be represented by IPublishedElement of IPublishedElement? Essentially meaning you could have a block editor (#12) that has a property of another block editor. Wouldn't that be more or less the same as the Grid?

@leekelleher
Copy link
Member

"How to tackle position data within the data model (row x, col y)?"

As an out of left field idea, how's about treating the elements as referential data?
Where each different block-based editor could be responsible for their own "layout" data structure, and reference element data by UDI (from an inline "blocks" repository)?

e.g.

{
	"layout": {
		// This is specific to the editor.
	},
	"blocks": [{
			"settings": {
				"_type": "umb://doctype/00000000000000000000000000000000",
				"_name": "Config 1",
				"_udi": "umb://element/99999999999999999999999999999999",
				"somePropertyAlias": "Some config"
			},
			"content": {
				"_type": "umb://doctype/11111111111111111111111111111111",
				"_name": "Content 1",
				"_udi": "umb://element/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
				"somePropertyAlias": "Hello world"
			}
		},
		// ... other content block data ...
	]
}

Then, say, a Nested Content-style editor could have a flat (single-dimensional) array, e.g.

{
	"layout": {
		"items": [
			"umb://element/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
			"umb://element/bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
			"umb://element/cccccccccccccccccccccccccccccccc"
		]
	},
	"blocks": [ /* snipped for brevity */ ]
}

The successor to the Grid, could then use a more complex (multi-tiered) "layout", e.g.

{
	"layout": {
		"rows": [{
				"items": [
					"umb://element/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
				]
			}, {
				"items": [
					"umb://element/bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
					"umb://element/cccccccccccccccccccccccccccccccc"
				]
			}
		]
	},
	"blocks": [ /* snipped for brevity */ ]
}

The benefit here would be that the content "blocks" could be preserved if the editor was switched, (in terms of interoperability). It would be down to the editor itself to figure out how to initialize its own "layout" and how to handle any existing "blocks" data. e.g. Switching from Grid v2 to Nested Content v2, it could take the "blocks" as the intended order, displaying them accordingly.


Like I say, I appreciate that this is a pretty left-field idea - ignore if it doesn't spark-joy! 😁

@Shazwazza
Copy link
Contributor

Hi @leekelleher That's very interesting!

To clarify:

from an inline "blocks" repository

What you are referring to is the elements inside of your blocks property which are json representation of content elements that will be assigned their own UDIs... correct? (makes sense)

Then in the "layout" property, the editor can store any structure it wants and then just reference items by their UDI (as stored in the blocks) property. (also makes sense)

So for each value saved for a property type it would store a json structure:

{
   "layout" : {},
   "blocks" : []
}
  • layout contains a json structure of the editors choosing with UDI references to the elements inside of 'blocks'
  • blocks contains elements of settings + content and each content item has it's own UDI

It seems to make sense to me and offers the most flexibility for property editors to arrange the content how it wants to.

Anyone have feedback on this?

@kjac
Copy link
Contributor

kjac commented Jul 10, 2019

Sounds like a great idea to introduce a layouting level of data, even if this slightly defeats the initial purpose of having data stores in a layout/use case specific agnostic way.

Would each editor then have to create their own proprietary data structure around the stored data for rendering purposes?

@leekelleher
Copy link
Member

@Shazwazza Thanks for validation on the idea - all correct! 👍

@kjac Yes, it'd be up to each editor to control their own layout. The likes of Nested Content / Archetype / Stacked Content would be single-dimensional arrays, theoretically hot-swappable. A complex editor would be trickier... I guess that's one of those unknown unknowns.

@skttl
Copy link

skttl commented Jul 10, 2019

I like the seperation of layout and blocks.

layout could be optional too, I don't think that would be neede for the simple editors like Nested Content etc.

@nathanwoulfe
Copy link

I don't see any issue with not always being able to swap between editors - while it might be possible, technically, to build a mechanism to manage shifting between single and multi dimensional layouts, in reality it's probably not necessary as the content wouldn't work. Switching a single column ala stacked content into a complex grid layout wouldn't work without restructuring the content so that it actually makes sense when rendered.

Absolutely it should be possible to change the editor and preserve all the existing content, but if the actual layout of that content needs to be massaged some, I don't think that's an issue. It would be remiss to assume how content should reflow when changing editors, and should be left to a human to manage.

All that said, could it be an option that layout stores a collection of layouts, for different editors?

layout: {
    grid: ... some layout data for grid
    stackedContent: ... some layout data for stacking
    ....
}

That would allow swapping between stored layouts. I might want a simple single column 90% of the time, but during a particular campaign want to swap to a more complex grid for a week, before reverting to the previous layout, without having to revert changes to the page and potentially lose other changes.

@Shazwazza
Copy link
Contributor

@nathanwoulfe This is also an interesting idea! So if you did swap, the original layout is preserved because we would always save layouts with the key for the current editor. I guess the the overhead that this adds is that if a block is deleted we'd just have to delete that block from all layouts, but that's pretty simple. I like it and it's very simple!

@callumbwhyte you are around? Would love for you to give us some feedback on this approach. I would like to understand how this would affect the block editor (#12). It probably doesn't since AFAIK the block editor would just store an array in the layout property (of which you could have nested block editor arrays). But i think the layout idea provides the most flexibility.

@Migaroez
Copy link

When I read @leekelleher 's idea, The additional Idea from @nathanwoulfe came to mind as well.
It also allows for converters to be written.
So that when a swap is done from compatible editors, the layout is auto generated. This would have to be implemented by either the original or the new editor.
This would also solve the the possible of issues with swapping from A to B, adding data, and changing back to A.
Human interaction will always be necessary to make it just right. But I think it could lessen the hassle, and this newest data structure proposal supports it.

@skttl I think even nested content would need its layout, just for ordering. If you are going to separate layout from data, then the order of the data should not be in the data section. Because then you can get unexpected results when going from multidimensional to a single dimension.

@Shazwazza
Copy link
Contributor

Hi all - Just a heads up that I'm going to close this PR tomorrow and open up a new one with an up-to-date RFC that contains the relevant information discussed in this thread. This isn't normally the way things will work with the RFC process but I cannot get in touch with @callumbwhyte who would need to give me access to edit this PR so I need to start a new one. Once that's created we'll apply a one week timeframe for closing the RFC.

@Shazwazza Shazwazza added the final-comment-period Indicates that the RFC will be closed in a disclosed amount of time label Jul 22, 2019
@Shazwazza
Copy link
Contributor

Hi all,

I've updated the PR/RFC along with the link in the title to point to the latest document version. I've applied the "final comment period" tag with a 1 week timeframe to this RFC. I think the details of this RFC are great but please let me know if there's anything critical you feel is missing and we can adjust if necessary. Thanks for all the great ideas :)

@skttl
Copy link

skttl commented Jul 22, 2019

Looks good.

I think the property names in the layouts collection should match the editor aliases. I think it would be Umbraco.NestedContent for example.

Also, making v2's of every existing block editor, and deprecating the old ones is a good way of handling the change in my opinion.

@skttl
Copy link

skttl commented Jul 22, 2019

One last thing. Isnt udi://content used for nodes in the tree already? We could make them udi://block/ (or element) to limit confusion.

@leekelleher
Copy link
Member

+1 on using the property-editor alias as the layout key.

+1 on "element" type for the UDI, e.g. "umb://element/fffba547615b4e9ab4ab2a7674845bc9". Element over block, following the existing convention that a DocType has an "Is Element type?" option.

@Shazwazza
Copy link
Contributor

I have updated with the above, thanks!

@Shazwazza Shazwazza merged commit 058a24d into umbraco:master Jul 31, 2019
@Shazwazza
Copy link
Contributor

Thanks all! I have merged/accepted this RFC now 🎉

@Shazwazza Shazwazza added accepted and removed final-comment-period Indicates that the RFC will be closed in a disclosed amount of time labels Jul 31, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants