Enhancing the Authoring Experience: Extending the LinkItem

The LinkItem field is one of the most demanded properties by the community, allowing editors to effortlessly create and manage links across pages and blocks. It was introduced on CMS 12 – EPiServer.CMS 12.11.0 and this blog post will show how it can be extended to have a better authoring experience.

This is how you define the property in code:

[CultureSpecific]
[Display(Name = "Primary Link", GroupName = TabNames.Content, Order = 10)]
public virtual LinkItem Link { get; set; }

And this is how the editor will see the property

When you click on “Add Link” you will see a dialog where you can create/edit the link. You are able to select an internal page and add an external or even a media asset.

Here, you can notice that Text is required and will be used as the link text.

Extending the LinkItem Field: Empowering the Text field

What if we want to have a pre-defined list of link text and have those options as part of the Text field in the modal to keep consistency across links. Things like “Read More”, “Learn More”, “Go”, etc. can be somehow authored and then the editor can select the text from a list.

This is also useful when you have a multi-lingual site and you need to translate every link text. It saves a lot of time to have a list of link text with all the translations and have the editor select from it. Obviously, if a specific text is required, the author should be able to add a custom one.

Link Text Authoring Experience

We use the Settings feature and an IList of strings to maintain this content. You can apply a different approach to store the list of text that we are going to use.

[SettingsContentType(DisplayName = "Site Settings",
    GUID = "ff69f929-c91b-a6cb-9829-2ecf9d11e13c"
    SettingsName = "Site Settings")]
public class SiteSettings : SettingsBase
{
   ....

   [CultureSpecific]
   [Display(Name = "Global Link Text", GroupName = 
   TabNames.Content, Order = 200)]
    public virtual IList<string> LinkTextList { get; set; }
   ....
}

The editor will author a list of strings and can have as many as it’s needed

By using the built-in auto-suggestion editor feature (https://docs.developers.optimizely.com/content-management-system/docs/built-in-auto-suggestion-editor), we need to create a “Selection Factory” to retrieve all those options from the settings and return it as a list of Select Items. Remember, this class needs to inherit from ISelectionQuery and not from ISelectionFactory.

namespace Website.CMS.SelectionFactories
{
    [ServiceConfiguration(typeof(ISelectionQuery))]
    public class LinkTextSelectionQuery : ISelectionQuery
    {
        private readonly IEnumerable<SelectItem> _items;

        public LinkTextSelectionQuery()
        {
            var linkTexts = SettingsHelper.SiteSettings.GlobalLinkText != null ? SettingsHelper.SiteSettings.GlobalLinkText.ToList() : new List<string>();

            var list = new List<SelectItem>();
            list.Add(new SelectItem() { Text = string.Empty, Value = string.Empty });
            
            list.AddRange(linkTexts.Select(x => new SelectItem
            {
                Text = x,
                Value = x
            }));

            _items = list;
        }

        public IEnumerable<ISelectItem> GetItems(string query) => _items.Where(i => i.Text.StartsWith(query, StringComparison.OrdinalIgnoreCase));

        public ISelectItem GetItemByValue(string value) => _items.FirstOrDefault(i => i.Value.Equals(value));
    }
}

The list of items will be retrieved every time the LinkItem property needs to be rendered on a page or block.

Now, we need to extend our LinkItem modal, which is essentially a LinkModel under the hood

[EditorDescriptorRegistration(TargetType = typeof(LinkModel), EditorDescriptorBehavior = EditorDescriptorBehavior.PlaceLast)]
internal class CustomLinkEditorDescriptor : EditorDescriptor
{
    public override void ModifyMetadata(ExtendedMetadata metadata, IEnumerable<Attribute> attributes)
    {
        foreach (var modelMetadata in metadata.Properties)
        {
            var property = (ExtendedMetadata)modelMetadata;
            if (property.PropertyName == "Text")
            {
                property.ClientEditingClass = "epi/shell/form/SuggestionSelectionEditor";
                modelMetadata.EditorConfiguration["storeurl"] =
                    "/EPiServer/Shell/stores/selectionquery/Website.CMS.SelectionFactories.LinkTextSelectionQuery/";
            }
        }
    }
}

Setting the ClientEditingClass is affecting the UI of the text property and converts it from an input field to a Dropdown with auto-suggest feature.
The EditorConfigurator is what does the trick to populate the dropdown with the values retrieved on the class: Website.CMS.SelectionFactories.LinkTextSelectionQuery

Once you run the solution, and add a link, you will notice that the behavior of the link text (which was previously a single input text) has changed.

Click on the dropdown and you will see the authored list in the settings as prepopulated values:

With this feature, you can also filter the words you need. i.e. “Read” and it will suggest the options:

You can also add your own custom text for specific cases:

This is the final look of the Edit Link dialog

Hope this helps your editor to enhance the experience when creating links.

More resources:
https://world.optimizely.com/blogs/bartosz-sekula/dates/2022/6/built-in-support-for-single-linkitem-property/
https://jakejon.es/blog/adding-a-telephone-link-option-to-the-episerver-link-editor
https://world.optimizely.com/blogs/Anders-Hattestad/Dates/2015/2/extending-the-hyperlink-with-custom-field/

Leave a comment