November Happy Hour will be moved to Thursday December 5th.

Problem with customizing the new TinyMCE editor for base classes

Vote:
 

I am implementing the new TinyMCE and in this big solution we have some abstract baseclasses where we have common properties shared with around 50 different content types.

Some of those properties were using a simple version of tiny with less functions and when I try to fix this with the technique described here:
https://world.episerver.com/documentation/developer-guides/CMS/add-ons/customizing-the-tinymce-editor-v2/

If works when I write (a content type)
config.For(x => x.TeaserBody, simplified);

But not when I write like this (a base typ)
config.For(x => x.TeaserBody, simplified);

Does anyone know if this new way of defining specific versions for different properties is suppose to work for base classes?

#191253
Edited, Apr 24, 2018 17:00
Vote:
 

Hi Henrik, looked at the 2.1.0 version code and it uses the content type and property name as a key to fetch the settings, so I would say it doesn't work with base types or interfaces.

#191257
Apr 24, 2018 18:33
Vote:
 

Thanks, was just about to start digging into the source code.

Then this need to be a feature request since I believe I am not alone to use base classes for common properties....

#191298
Apr 24, 2018 20:49
Vote:
 

Hi,

Thanks for the input. We have been thinking about this feature but we decided not to do it in the first release as we wanted some feedback first on how you want it to work. 

Should it work on for every base type in the inheritance chain (override the settings on different levels)?  

E.g. 

abstract class MainPageData
{
public virtual XHtml MainBody {get;set;}
}

class ProductPage : MainPageData {

}

class VerySpecialProductPage : ProductPage
{

}

class AndEvenMoreSpecialProductPage : VerySpecialProductPage {

}

// Init
context.Services.Configure<TinyMceConfiguration>(config =>
{
config.For<MainPageData>(t=>t.MainBody)
.Toolbar("cut");

config.For<VerySpecialProductPage>(t=>t.MainBody)
.Toolbar("cut past");
});

ProductPage would have cut
VerySpecialProductPage would have cut copy
AndEvenMoreSpecialProductPage would have cut copy

Or should we just fallback to the settings on the abstract base types?

And what happens if we throw interfaces in mix.

#191399
Apr 24, 2018 21:47
Vote:
 

Haven't tried but I guess for now it's quite easy to use reflection, iterate based on parent or interface and set for the "leaf" type on your own?

#191502
Apr 24, 2018 22:42
Vote:
 

@Magnus, would be nice if it can be done with base classes, interfaces would be a bonus but complex logic will there be in extreme cases where there is base class and interface matching.

I'll throw in another idea which could work pretty well with the current way. We could register named configurations, like first we naturally define the settings for TinyMce config and then we could call something like Register("SimpleEditorSettings", config), then we could use attribute to decorate a property like this:

[TinyMceConfig("SimpleEditorSettings")]
public virtual XhtmlString SomeProperty {get;set;}

Then we could use that in base classes as the attribute would be inherited for the classes inherited from the base class. Also one could change the editor in derived class by applying a new attribute.

And logic could be something like: first check from the current type is the property decorated with attribute if yer use that, next is there a configuration with the current way of doing content type + property name key, if yes use that, next check is there attribute coming from some base class if yes use that otherwise use the default.

Something like that ;) So the closest configuration would always win, attribute on the type has highest priority, next the current way of doing and last the attribute on the base class.

PROS

  • its explicit with attribute
  • easy to reuse the same configuration without any class inheritance chain
  • base class properties can be decorated with attribute and by default inheriting classes will get the same settings for the property
  • there will be no situation that why the property is getting wrong config (because of some base class / interface)
  • with attribute a deriving class can overwrite easily the editor settings defined in base class

CONS

  • you don't have the configuration in one place
  • you need to decorate properties to get inheritance
  • if you need to change config, for some properties then you need to do a search and replace on a string to change the attribute config value and hope that you don't by mistake change a config for property that you should not have changed
#191525
Apr 25, 2018 6:40
Vote:
 

Magnus I was just about to write pretty much the same as Antti, so thanks Antti for writing it so good!

I liked the way it was before with attributes and would very much have that possibility again.

If that will not be possible then I would prefere to have it that the closest setting win, so if there are one setting on a base class and then there is also another setting on the contenttype then the one on the content type wins

#191528
Apr 25, 2018 8:57
Vote:
 

"if you need to change config, for some properties then you need to do a search and replace on a string to change the attribute config value and hope that you don't by mistake change a config for property that you should not have changed"

First, you can use constants instead of magic strings. But even better, as the old settings worked for TinyMCE, is to use a type instead, like this:

[TinyMceConfig(typeof(SimpleEditorSettings))]
public virtual XhtmlString SomeProperty {get;set;}

But I guess there is a good reason Episerver moved away from this approach?

#191531
Apr 25, 2018 9:08
Vote:
 

Yes of course constants would be used for the registered names (my sample was just to demonstrate the idea). I guess we can't use that typeof(SimpleEditorSettings) as we are not going to create new types for the different configurations?

#191536
Apr 25, 2018 9:26
Vote:
 

Settings are an instance of an TinyMceSettings object. So I guess you could inherit from this class to create different types of settings and then reference them by type in an attribute somehow.

#191537
Apr 25, 2018 9:29
Vote:
 

In that case it would work, maybe the question is do we want to create different types just to achieve this? I guess not a biggie if you have a couple of configurations (most likely two configurations might be what sites usually would have) but if you go crazy with the configurations then you end up creating a lot of types so that you can use typeof on a type (and most likely not adding any new functionality to type). PROS - you can use find references in VS, CONS - you need to create new types just for this.

I personally wouldn't want to create new types. Currently we can use the Clone functionality if we want to use some setting as the base for another configuration.

But ideas ideas and then let us vote on the options? ;-)

#191540
Apr 25, 2018 9:45
Vote:
 

In most cases you just change the default settings, i.e. no need for an attribute, and then have one more locked down setting for introduction texts and so on. I can't imagine that one would create a lot of different settings. 

#191544
Apr 25, 2018 9:51
Vote:
 

Hi,
We have talked some more about this now and have a suggestion.

But first, the question about the Attributes on the DataModels.
We have a long term plan to try to get rid of the attributes that describes what the edit ui should look like from the data models. So most likely we are not going to add new attributes for this.


We think that the idea (that I posted earlier) will solve your problem in a
pretty good way and still have all the configuration in the same place.

abstract class MainPageData
{
    public virtual XHtml MainBody {get;set;}
}

class ProductPage : MainPageData {

}

class VerySpecialProductPage : ProductPage
{

}

class AndEvenMoreSpecialProductPage : VerySpecialProductPage {

}

// Init
context.Services.Configure<TinyMceConfiguration>(config =>
{

    // This setting will be based on Default()
    config.For<MainPageData>(t=>t.MainBody)
        .Toolbar("cut");

    // This setting will be based on Default() to make sure the API is consistent
    config.For<VerySpecialProductPage>(t=>t.MainBody)
        .Toolbar("cut past");
    });
})



ProductPage would have cut
VerySpecialProductPage would have cut copy
AndEvenMoreSpecialProductPage would have cut copy

We also said that we will not create a copy of the settings configured on MainPageData
when we request the settings for MainBody on ProductPage. They will share the same settings instance, until you decide to modify it on the ProductPage.

And to be able to do this without doing a breaking change this new behavior needs to be opt-in. That means that we probably will add a
property on the TinyMceConfiguration settings that you can set to true to enable it.
e.g.

context.Services.Configure<TinyMceConfiguration>(config =>
{
    // Will be true for every TinyMceSetting
    config.InheritSettings = true;
});



#191549
Apr 25, 2018 10:49
Vote:
 

Sounds like you had it already a bit further thought ;) I think the most important part is that it is documented how the "resolving" rule goes (like you already did) and no cumbersome if this and that then do something else is added to the resolving.

Is this released next week? *wink wink*

#191551
Apr 25, 2018 11:31
Vote:
 

Maybe something like this would also be nice to be able to get some own convention over configuration in place.

config.For<IContent>("*Body", someConfig)
config.For<IContent>("*Intro", someOtherConfig)

#191798
Edited, Apr 27, 2018 22:37
Vote:
 

Not sure if you noticed but we have updated the api a bit to make it easier for you to do this.

https://world.episerver.com/blogs/magnus-stalberg/Dates/2018/5/tinymce-configuration-api-news/

#192692
May 23, 2018 9:10
* You are NOT allowed to include any hyperlinks in the post because your account hasn't associated to your company. User profile should be updated.