Configure TinyMCE Editor Toolbar through code

Vote:
 

Hi, I see from http://webhelp.episerver.com/cms/7.1/en/Content/CustomizeEditMode/SS_GlobalSettings.htm that through soft config in the CMS user interface it is possible to customise the toolbar for XHTMLString properties.

Is it possible to have this level of control in the C# code of my website? I.e. in some app startup code or even better declaratively through attributes on a defined page or block content type?

Thanks.

#89532
Aug 19, 2014 15:34
Vote:
 

Unfortunately there is no attribute you could use in your models to decorate Xhtml properties with. However youcould probably create one on your own. It's actually quite simple to create settings from code and add them to properties. I will probably write a blog post about it soon and make it as an attribute. In the meanwhile you could use following code, e.g. in an initializable module.

First create the setting:

var defaultSettings = new TinyMCESettings()
{
    Width = 580,
    Height = 300,
    ToolbarRows = new List()
    { 
        new ToolbarRow(new []
        {
            "bold", 
            "italic",
            "separator", 
            "numlist", 
            "bullist",
            "outdent", 
            "indent",
            "epiquote", 
            "separator", 
            "undo", 
            "redo",
            "separator", 
            "epilink",
            "unlink",
            "anchor",
            "separator", 
            "image", 
            "epiimageeditor", 
            "separator", 
            "pastetext", 
            "cleanup", 
            "removeformat", 
            "separator", 
            "code", 
            "fullscreen"
        }),
        new ToolbarRow(new []
        {
            "formatselect", 
            "styleselect",
            "table", 
            "row_props", 
            "cell_props", 
            "separator", 
            "row_before", 
            "row_after", 
            "delete_row", 
            "separator", 
            "col_before", 
            "col_after", 
            "delete_col", 
            "separator", 
            "split_cells", 
            "merge_cells"                      
        }) 
    }
};

Then you need to save it, in this case we will save it as a global setting and not just on a property:

var propertySettingsRepo = ServiceLocator.Current.GetInstance();

var settingsWrapper = propertySettingsRepo.GetGlobals(typeof(TinyMCESettings))
        .FirstOrDefault(s => s.DisplayName == "Default");

if (settingsWrapper == null)
{
    settingsWrapper = new PropertySettingsWrapper();
}

settingsWrapper.DisplayName = "Default";
settingsWrapper.IsDefault = true;
settingsWrapper.IsGlobal = true;
settingsWrapper.PropertySettings = defaultSsettings;

propertySettingsRepo.SaveGlobal(settingsWrapper);

Then we need to apply it to a property, this is however not necessary in our case because the IsDefault is set to true and will apply to all Xhtml properties anyways:

var propertyNames = new[] { "MainBody", "MainIntro" };
var propertyDefinitionRepo = ServiceLocator.Current.GetInstance();
var contentTypes = ServiceLocator.Current.GetInstance().List();

foreach (var contentType in contentTypes)
{
    var propertyDefinitions = contentType.PropertyDefinitions.Where(pd =>
            propertyNames.Any(propertyName => propertyName.Equals(pd.Name, StringComparison.OrdinalIgnoreCase)));

    foreach (var property in propertyDefinitions.Where(definition => definition.SettingsID == Guid.Empty))
    {
        var settingsContainer = new PropertySettingsContainer(Guid.Empty);

        settingsContainer.AddSettings(settingsWrapper);
        propertySettingsRepo.Save(settingsContainer);

        var writeableClone = property.CreateWritableClone();
        writeableClone.SettingsID = settingsContainer.Id;
        propertyDefinitionRepo.Save(writeableClone);
    }
}

Hope this helps!

#89554
Aug 20, 2014 4:39
Vote:
 

Did it work Daniel?

#89634
Aug 21, 2014 10:42
Vote:
 

This could also be solved by only creating an EditorDescriptor that sets your programmatically created setting. No need to save it.

A blog post about this will come up this weekend.

#89682
Aug 22, 2014 9:17
Vote:
 

Thanks Johan and Alf - we'll give this a go and let you know.

In the meantime you guys might like to see the latest EPiServer 7.5 sites's we've released:

...all on the latest 7.11 drop, using MVC 4 and extensive use of blocks, with an automated testing framework that mocks the various repositories in EPiServer to allow us to automate the functionality of our pages and blocks.

Loving EPiServer :)

#89685
Aug 22, 2014 9:29
Vote:
 

Hi!

I can mention that we have been working on adding native support for this in the platform and unless we find some blocking issues during testing, this should be added during the coming month or so.

#89695
Aug 22, 2014 11:38
Vote:
 

That feature is long overdue Linus :) Thank you for finally adding it.

#89715
Aug 22, 2014 15:44
Vote:
 

Hi Alf

Any news on the blog post on creating EditorDiscriptor for TinyMCE custom settings?

#90162
Sep 03, 2014 11:06
Vote:
 

Hi, Sorry I ran into some problems about it in 7.5 comparing to my approach in 7.0/7.1

Need to gather my thoughts to EPiServer to see how the approach could for 7.5

Thanks for the reminder.

#90163
Sep 03, 2014 11:08
Vote:
 

The code for the main product functionality is pretty much done. However, since the CMS core and UI packages has been split into separate releases and the new functionality affects both, it's currently in the process of being added to both packages. This might take a few weeks but hopefully, it will be out in September.

#90187
Sep 03, 2014 13:00
Vote:
 

Hi Linus, Alf

Thank you both for the update. I have managed to do it using a Custom Editor Descriptor.

#90188
Edited, Sep 03, 2014 13:02
Vote:
 

Has this been added to EPiServer now? If so, how do I do it, some kind of attribute? 

Currently i've implemented it with an EditorDescriptor but I get an 404 on epiexternaltoolbar, if I remove that plugin with code, everything works as it should. When looking in the CMS.zip I can see that the folder epiexternaltoolbar doesn't exists in the location epi/TinyMCE is looking, the folder is located in the CMS.ZIP/Util/Editor/tinymce/plugins/epiexternaltoolbar folder but Epi is looking in the CMS.zip/9.4.4.0/ClientResources/editor/tiny_mce/plugins folder(where it doesnt exists).

Heres my code(with the epiexternaltoolbar workaround), basically a modified version of this https://gist.github.com/mattiasnorell/0daf1d851358ecb01f8c.


public class EditorButtonEditorDescriptor : EditorDescriptor
{
    private const string Plugins = "plugins";
    private const string EpiExternalToolBar = "epiexternaltoolbar";

    public override void ModifyMetadata(ExtendedMetadata metadata, IEnumerable<Attribute> attributes)
    {
        var attributeValue = metadata.Attributes.OfType<EditorButtonsAttribute>().SingleOrDefault();
        if (attributeValue == null) return;

        var property = (PropertyData) metadata.Model;
        var content = metadata.FindOwnerContent();

        var settings = content.GetPropertySettings<TinyMCESettings>(property.Name);
        settings.ToolbarRows.Clear();
        settings.ToolbarRows.Add(attributeValue.ToolbarRow);

        var options = new TinyMCEInitOptions(TinyMCEInitOptions.InitType.EditMode, settings, content);
        if (options.InitOptions.ContainsKey(Plugins))
        {
            var plugins = (string)options.InitOptions[Plugins];
            options.InitOptions[Plugins] = RemoveEpiExternalToolbar(plugins);
        }
            
        metadata.EditorConfiguration[Plugins] = options.InitOptions;

        base.ModifyMetadata(metadata, attributes);
    }

    private static string RemoveEpiExternalToolbar(string plugins)
    {
        if (plugins.Contains(EpiExternalToolBar))
        {
            var pluginsList = plugins.Split(',').ToList();
            pluginsList.Remove(EpiExternalToolBar);
            return string.Join(",", pluginsList);
        }

        return plugins;
    }
}
[AttributeUsage(AttributeTargets.Property)]
public class EditorButtonsAttribute : Attribute
{
    private ToolbarRow _toolbarRow = new ToolbarRow();

    public EditorButtonsAttribute(params string[] toolbarRowButtons)
    {
        foreach (var toolbarRowButton in toolbarRowButtons)
        {
            _toolbarRow.Buttons.Add(toolbarRowButton);
        }
    }

    public ToolbarRow ToolbarRow
    {
        get { return _toolbarRow; }
        set { _toolbarRow = value; }
    }
}
public class EditorialBlock : SiteBlockData
{
    [Display(GroupName = SystemTabNames.Content)]
    [CultureSpecific]
    [EditorButtons(new[] { "bold" })]
    public virtual XhtmlString MainBody { get; set; }
}




#147749
Edited, Apr 23, 2016 15:30
Vote:
 

Thanks for sharing these worthy codes.

exercise classes

#154293
Aug 30, 2016 10:23
Vote:
 

Did anyone ever manage to achieve this?

Josef-Ottosson's code isn't working for me - the TinyMCE renders with the default toolbar. The "epiexternaltoolbar" plugin wasn't being loaded for me (guessing it's a bug resolved in a later version of the CMS).

Also, this code didn't work either: https://gist.github.com/mattiasnorell/0daf1d851358ecb01f8c

It just renders a readonly version of the TinyMCE editor, with no toolbar options (regardless of what you pass in via the attribute).

#181358
Aug 16, 2017 17:24
Vote:
 

Yes, this should work OOTB now.

[ServiceConfiguration(ServiceType = typeof(PropertySettings))]
public class SimpleTinyMceSettings : PropertySettings<TinyMCESettings>
{
    public SimpleTinyMceSettings()
    {
        this.DisplayName = "Simple settings";
    }

    public override Guid ID
    {
        get
        {
            return new Guid("326c8ae8-685d-4387-872a-006fb45818d5");
        }
    }

    public override TinyMCESettings GetPropertySettings()
    {
        return new TinyMCESettings
        {
            ContentCss = "/css/editor.css",
            Width = 580,
            Height = 300,
            //// NonVisualPlugins = new[] { "optimizededitor", "contextmenu" },
            ToolbarRows = new List<ToolbarRow>
            {
                new ToolbarRow(new[]
                {
                    TinyMCEButtons.Bold,
                    TinyMCEButtons.SuperScript,
                    TinyMCEButtons.SubScript,
                    TinyMCEButtons.Separator,
                    TinyMCEButtons.Undo,
                    TinyMCEButtons.Redo,
                    TinyMCEButtons.Separator,
                    TinyMCEButtons.EPiServerLink,
                    TinyMCEButtons.Unlink,
                    TinyMCEButtons.Anchor,
                    TinyMCEButtons.Separator,
                    TinyMCEButtons.PasteText,
                    TinyMCEButtons.CleanUp,
                    TinyMCEButtons.RemoveFormat,
                    TinyMCEButtons.Separator,
                    TinyMCEButtons.Search,
                    TinyMCEButtons.Replace,
                    TinyMCEButtons.Separator,
                    TinyMCEButtons.Code,
                    TinyMCEButtons.Fullscreen
                })
            }
        };
    }
}

And then decorate your properties like this:

[PropertySettings(typeof(SimpleTinyMceSettings))]
[Display]
public virtual XhtmlString TeaserBody { get; set; }

You can also make the setting default for all XHTML properties (those without PropertySettings attribute) by setting this.IsDefault = true; in the constructor.

#181359
Aug 16, 2017 17:29
Vote:
 

That works, excellent.

Thanks Johan - may be worth you writing a blog post about that :-)

#181360
Aug 16, 2017 17:38
Vote:
 

Or just read the manual https://world.episerver.com/documentation/developer-guides/CMS/Content/Properties/Property-settings/ ;)

#181361
Aug 16, 2017 17:40
* 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.