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

Customizing Tinymce for multi-site solution

Vote:
 

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?

#196277
Aug 23, 2018 21:41
Vote:
 

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 }
    };
}
#196299
Aug 24, 2018 9:49
Vote:
 

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

#196315
Aug 24, 2018 14:22
Vote:
 

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).

#196316
Aug 24, 2018 14:43
Vote:
 

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.

#201257
Feb 11, 2019 15:17
Vote:
 

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. :)

#201259
Edited, Feb 11, 2019 16:14
Vote:
 

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/

#201276
Feb 12, 2019 7:38
Vote:
 

The keys are just new strings. :)

#201277
Feb 12, 2019 8:03
Vote:
 

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."

#201282
Feb 12, 2019 8:24
Vote:
 

Oh, they don’t reveal the keys? ;) I guess you have to put your Sherlock coat on and fire up dotPeek or similar.

#201292
Feb 12, 2019 13:34
Vote:
 

It's always on and I will!

#201294
Feb 12, 2019 15:08
* 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.