Article Extending the Content Editor

The platform allows developers to extend the content editor to provide for richer content.

Why Extend the Content Editor?

The content editor allows the input of html, images and video, however other types of content cannot be entered or would be difficult to enter for end users.  The platform provided extensions to the content editor are good use cases for what extending the content editor can provide.

Source Code with Syntax Highlighting

Source code can be entered into the editor without an extension, but without an extension each user is responsible for providing the styling for the source code they enter.  By adding a source code extension to the editor, we can simplify the input required by the end user and provide a consistent output for code samples with syntax highlighting.

Polls

A Polls extension is also provided with the platform.  Without this extension Polls could not be entered using the editor.  The Polling extension is able to provide an interface to allow poll entry, rendered those polls and store the data custom set of database tables.

Creating an Extension

The platform provides the IEmbeddableContentFragmentType interface, it can be found in the Telligent.Evolution.Api.Dll.  This interface allow new types of content to be entered into the editor.

This example will create a new EmbeddableContentFragmentType that displays a box inside the content.  The background color of the box will be configurable when inserting the content.

First, we need to provide a unique id (EmbeddedContentFragmentTypeId), name (ContentFragmentName) and description (ContentFragmentDescription) for our content fragment type.  We will use the ITranslatablePlugin interface to provide translations for the name and description.

readonly Guid _fragmentTypeId = new Guid("6c33babb0c0e45e8b691294b8b6569e5");

public string ContentFragmentName
{
    get { return _translation.GetLanguageResourceValue("fragment_name"); }
}

public string ContentFragmentDescription
{
    get { return _translation.GetLanguageResourceValue("fragment_description"); }
}

public Guid EmbeddedContentFragmentTypeId
{
    get { return _fragmentTypeId; }
}

We can limit the types the content that can be used when embedding our new content fragment type.  In the example we will allow all types, so we will just return true.  If we wanted to limit our new type to only be allowed in blog posts, we could return true only when the contentTypeId parameter matches the blog post content type id.

bool IEmbeddableContentFragmentType.CanEmbed(Guid contentTypeId, int userId)
{
    return true;
}

The EmbedConfiguration property is used to define the form for entering the data needed to define the embed.  In this case, we will allow the user to choose the background color of the embedded box, so we define a single property with the PropertyType of Color.

PropertyGroup[] IEmbeddableContentFragmentType.EmbedConfiguration
{
    get
    {
        var group = new PropertyGroup("options", _translation.GetLanguageResourceValue("configuration_group"), 1);
        group.Properties.Add(new Property("color", _translation.GetLanguageResourceValue("configuration_color"), PropertyType.Color, 1, ""));
        return new PropertyGroup[] { group };
    }
}

The Validate method is provided to verify that the data entered by the end user is correct.  A translated error may be returned to the end user to assist in correcting any invalid data.

EmbeddableContentFragmentValidationState IEmbeddableContentFragmentType.Validate(IEmbeddableContentFragment embeddedFragment)
{
    var color = embeddedFragment.GetString("color");

    if (string.IsNullOrEmpty(color))
        return new EmbeddableContentFragmentValidationState(false) { Message = _translation.GetLanguageResourceValue("configuration_colorinvalid") };

    return new EmbeddableContentFragmentValidationState(true);
}

The AddUpdateContentFragments method is called whenever embedded content is added or updated.  This can be used to create, update and delete database records for the embeds.  This example does not need to use this method, so we will leave it blank.

void IEmbeddableContentFragmentType.AddUpdateContentFragments(Guid contentId, Guid contentTypeId, IEnumerable<IEmbeddableContentFragment> embeddedFragments)
{

}

The preview image will be displayed as an icon in the editor menu and in the editor to represent the embedded content.  For this example, we can just leave this blank and allow the platform to provide an image.

string IEmbeddableContentFragmentType.PreviewImageUrl
{
    get { return null; }
}

The Render method is called when the embedded content is being output.  A target parameter is also provided, this can be useful if you want to display different output for different targets.  The example will read the color parameter we defined in the EmbedConfiguration property.  If the color has been set, we will render a div with a fixed height and width and the selected background color.

string IEmbeddableContentFragmentType.Render(IEmbeddableContentFragment embeddedFragment, string target)
{
    var color = embeddedFragment.GetString("color");

    if (String.IsNullOrEmpty(color))
        return String.Empty;

    return String.Format("<div style='height: 300px; width: 300px; background-color: {0};'> </div>", color);
}

Here is the completed source code for the Sample IEmbeddableContentFragmentType:

using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using Telligent.DynamicConfiguration.Components;
using Telligent.Evolution.Extensibility.EmbeddableContent.Version1;
using Telligent.Evolution.Extensibility.Version1;

namespace Samples
{
    public class SampleEmbeddable : IEmbeddableContentFragmentType, ITranslatablePlugin, IPlugin
    {
        readonly Guid _fragmentTypeId = new Guid("6c33babb0c0e45e8b691294b8b6569e5");

        ITranslatablePluginController _translation;

        #region IEmbeddableContentFragmentType Members
        public string ContentFragmentName
        {
            get { return _translation.GetLanguageResourceValue("fragment_name"); }
        }

        public string ContentFragmentDescription
        {
            get { return _translation.GetLanguageResourceValue("fragment_description"); }
        }

        public Guid EmbeddedContentFragmentTypeId
        {
            get { return _fragmentTypeId; }
        }

        bool IEmbeddableContentFragmentType.CanEmbed(Guid contentTypeId, int userId)
        {
            return true;
        }

        PropertyGroup[] IEmbeddableContentFragmentType.EmbedConfiguration
        {
            get
            {
                var group = new PropertyGroup("options", _translation.GetLanguageResourceValue("configuration_group"), 1);
                group.Properties.Add(new Property("color", _translation.GetLanguageResourceValue("configuration_color"), PropertyType.Color, 1, ""));
                return new PropertyGroup[] { group };
            }
        }

        void IEmbeddableContentFragmentType.AddUpdateContentFragments(Guid contentId, Guid contentTypeId, IEnumerable<IEmbeddableContentFragment> embeddedFragments)
        {

        }

        string IEmbeddableContentFragmentType.PreviewImageUrl
        {
            get { return null; }
        }

        string IEmbeddableContentFragmentType.Render(IEmbeddableContentFragment embeddedFragment, string target)
        {
            var color = embeddedFragment.GetString("color");

            if (String.IsNullOrEmpty(color))
                return String.Empty;

            return String.Format("<div style='height: 300px; width: 300px; background-color: {0};'>&nbsp;</div>", color);
        }

        EmbeddableContentFragmentValidationState IEmbeddableContentFragmentType.Validate(IEmbeddableContentFragment embeddedFragment)
        {
            var color = embeddedFragment.GetString("color");

            if (string.IsNullOrEmpty(color))
                return new EmbeddableContentFragmentValidationState(false) { Message = _translation.GetLanguageResourceValue("configuration_colorinvalid") };

            return new EmbeddableContentFragmentValidationState(true);
        }
        #endregion

        #region IPlugin Members
        string IPlugin.Description
        {
            get { return "Displays a box with configurable height."; }
        }

        void IPlugin.Initialize()
        {

        }

        string IPlugin.Name
        {
            get { return "Sample Embeddable"; }
        }
        #endregion

        #region ITranslatablePlugin Members
        Translation[] ITranslatablePlugin.DefaultTranslations
        {
            get
            {
                var translation = new Translation("en-us");

                translation.Set("fragment_name", "Embeddable Box");
                translation.Set("fragment_description", "Displays a box with configurable height.");

                translation.Set("configuration_group", "Options");
                translation.Set("configuration_color", "Background Color");
                translation.Set("configuration_colorinvalid", "A Background Color must be provided.");

                return new Translation[] { translation };
            }
        }

        void ITranslatablePlugin.SetController(ITranslatablePluginController controller)
        {
            _translation = controller;
        }
        #endregion
    }
}

Once the plugin has been deployed and enabled in the community, when creating a post you will see the option to "Insert Embeddable Box"

Select that option and you will be able to configure the background color

After submitting this form, the editor will display the embeddable content using the previewimageurl provided in the code.  After creating the post, you should see the box rendered with the chosen background color.