In a project I'm working on I used settings transforms in code to handle multi site content css and style formats. Here is a little example of that:
[ModuleDependency(typeof(TinyMceInitialization))] public class SiteTinyMceInitialization : IConfigurableModule { public void ConfigureContainer(ServiceConfigurationContext context) { context.Services.Configure<TinyMceConfiguration>(config => { var locator = ServiceLocator.Current; config.Default() .AddSettingsTransform("default-content-css-transform", (settings, content, propertyName) => { var contentCss = $"/public/tinymce-{SiteDefinition.Current.Name}.css"; settings.ContentCss(contentCss); }) .AddPlugin("anchor table code") .Toolbar( "epi-link anchor | image epi-image-editor | bold italic | alignleft aligncenter alignright | " + "styleselect | table | cut copy pastetext paste removeformat fullscreen | bullist numlist | " + "subscript superscript | undo redo find code" ) .AddSettingsTransform("default-style-select-transform", (settings, content, propertyName) => { settings.StyleFormats(DefaultStyleFormats[SiteDefinition.Current.Name]); }); }); } public void Initialize(InitializationEngine context) { } public void Uninitialize(InitializationEngine context) { } /// <summary> /// HappyME style select formats /// </summary> internal static object[] HappyMeDefaultStyleFormats = { new { title = "Heading", items = new[] { new {title = "Heading 1", format = "h1"}, new {title = "Heading 2", format = "h2"}, new {title = "Heading 3", format = "h3"}, new {title = "Heading 4", format = "h4"} } }, new { title = "Text size", items = new[] { new {title = "Normal", inline = "span", classes = "size--normal"}, new {title = "Large", inline = "span", classes = "size--large"} } }, new { title = "Font weight", items = new[] { new {title = "Light", inline = "span", classes = "weight--light"}, new {title = "Regular", inline = "span", classes = "weight--regular"}, new {title = "Semi Bold", inline = "span", classes = "weight--semi-bold"}, new {title = "Bold", inline = "span", classes = "weight--bold"}, new {title = "Extra Bold", inline = "span", classes = "weight--extra-bold"} } }, new { title = "Color", items = new[] { new {title = "Black", inline = "span", classes = "color--black"}, new {title = "Dark grey", inline = "span", classes = "color--dark-gray"}, new {title = "Grey", inline = "span", classes = "color--gray"}, new {title = "Brand", inline = "span", classes = "color--brand"} } }, new { title = "Background color", items = new[] { new {title = "Blue", inline = "span", classes = "bg--blue"}, new {title = "Green", inline = "span", classes = "bg--green"}, new {title = "Orange", inline = "span", classes = "bg--orange"}, new {title = "Pink", inline = "span", classes = "bg--pink"} } } }; /// <summary> /// OSL default style select formats /// </summary> internal static object[] OslDefaultStyleFormats = { new { title = "Heading", items = new[] { new {title = "Heading 1", format = "h1"}, new {title = "Heading 2", format = "h2"}, new {title = "Heading 3", format = "h3"}, new {title = "Heading 4", format = "h4"} } }, new { title = "Text size", items = new[] { new {title = "Normal", inline = "span", classes = "size--normal"}, new {title = "Small", inline = "span", classes = "size--small"}, new {title = "Large", inline = "span", classes = "size--large"} } }, new { title = "Font weight", items = new[] { new {title = "Extra Light", inline = "span", classes = "weight--extra-light"}, new {title = "Light", inline = "span", classes = "weight--light"}, new {title = "Regular", inline = "span", classes = "weight--regular"}, new {title = "Medium", inline = "span", classes = "weight--medium"}, new {title = "Semi Bold", inline = "span", classes = "weight--semi-bold"} } }, new { title = "Indent", items = new[] { new {title = "Small", inline = "span", classes = "indent--small"}, new {title = "Large", inline = "span", classes = "indent--large"} } } }; /// <summary> /// VitaePro default style select formats /// </summary> internal static object[] VitaeProDefaultStyleFormats = { new { title = "Heading", items = new[] { new {title = "Heading 1", format = "h1"}, new {title = "Heading 2", format = "h2"}, new {title = "Heading 3", format = "h3"}, new {title = "Heading 4", format = "h4"} } }, new { title = "Text size", items = new[] { new {title = "Normal", inline = "span", classes = "size--normal"}, new {title = "Medium", inline = "span", classes = "size--medium"}, new {title = "Large", inline = "span", classes = "size--large"} } }, new { title = "Font weight", items = new[] { new {title = "Regular", inline = "span", classes = "weight--regular"}, new {title = "Bold", inline = "span", classes = "weight--bold"} } }, new { title = "Color", items = new[] { new {title = "Black", inline = "span", classes = "color--black"}, new {title = "Grey", inline = "span", classes = "color--gray"}, new {title = "Brand", inline = "span", classes = "color--brand"} } }, new { title = "Other", items = new[] { new {title = "Question box", inline = "", block = "p", classes = "info-box"}, new {title = "Border bottom", inline = "span", block = "p", classes = "border--bottom"} } } }; public static IDictionary<string, object[]> DefaultStyleFormats = new Dictionary<string, object[]> { { SiteNames.HappyME, HappyMeDefaultStyleFormats }, { SiteNames.OSL, OslDefaultStyleFormats }, { SiteNames.VitaePro, VitaeProDefaultStyleFormats } }; }
Interesting, I didn't realize you could use the site context that way. I thought that it would be resolved one time at app startup and that was it. I'll give this a shot, thanks
Yes, it's easy to assume that. :) The settings transforms are registered during app startup but they are applied when a tinymce editor is being instantiated in edit mode (when you do have a site context).
Has anyone found a way to vary ContentCss on something more dynamic? My use case right now is what the old Dynamic Property setting solved. I have a branch of pages that have other typography but the content types are shared with the rest of the site.
Wouldn't that be possible with a settings transform as well? Something like this:
public interface ITinyMceContentCssPathSetting : IContent
{
string ContentCssPath { get; }
}
.AddSettingsTransform("dynamic-content-css", (settings, content, propertyName) =>
{
var closestContentCssSetting = contentLoader.GetClosestContent<ITinyMceContentCssPathSetting>(content);
if (!string.IsNullOrEmpty(closestContentCssSetting?.ContentCssPath))
{
settings.ContentCss(closestContentCssSetting.ContentCssPath);
}
})
public static T GetClosestContent<T>(this IContentLoader contentLoader, IContent content)
{
if (content is T)
{
return (T)content;
}
var current = contentLoader.Get<IContent>(content.ParentLink);
while (!current.ContentLink.CompareToIgnoreWorkID(ContentReference.RootPage))
{
if (current is T)
{
return (T)current;
}
current = contentLoader.Get<IContent>(current.ParentLink);
}
return default(T);
}
EDIT: It would probably be a better idea to write a method that finds the closest content that implements ITinyMceContentCssPathSetting AND has a value for ContentCssPath property. :)
Of course! I was a bit stuck in my thinking and missed the Content part of the first example. Very easy to get what I needed...
Are the keys you have in your examples existing ones or just new strings?
The documentation part where AddSettingsTransform is handled for reference:
https://world.episerver.com/documentation/developer-guides/CMS/add-ons/customizing-the-tinymce-editor-v2/configuration-api/
Cool. I wonder if there's a case for knowing and replacing an OOTB key's value? Docs: "You can also remove registered transformations if you know the key."
Oh, they don’t reveal the keys? ;) I guess you have to put your Sherlock coat on and fire up dotPeek or similar.
I have a client with a multi-site solution that wants to update to newer verison of tinymce and I'm hitting a snag. In their multi-site solution, there are multiple tinymce settings configured, which are then applied to each and every XhtmlString property in the solution. Largely these are to alter the ContentCss property for customized style dropdowns. I'm not seeing a way to handle this other than manually configuring each and every property for each and every content type in the solution. Is there some way to configure the settings for a Content type as a whole (without specifiying the properties) and then allow that to be inherited? Otherwise, is there some bit or reflection magic that I can do to accomplish this, or am I stuck doing this the hard way?