Static HTML widget

Techical step back for marketing step forward

Here it goes again

Short but full of pain article today. Based on a true story.

You start a new project thinking "This time I'll do it right and clean!" and five minutes later you see the designs to implement, with extremely tight deadline you literally have couple of hours per widget and as a cherry on top marketers want to personalize a few of these beautiful design-rich widgets. But for now they are not sure what exactly. 🤔 When these challenges try to corner you it's time to go dirty! ☢️☢️☢️

Reinventing the wheel

Kentico Portal Engine approach has a Static HTML web part and widget out-of-the-box. We used it quite a lot in the following circumstances:

  • The page it is used on is changing quite rarely, for example:
    • Company or history pages
    • Short time campaign landing pages with the unique bespoke design
    • Design-rich landing pages that live on the website almost forever
  • The content of the page is edited by developers only
  • Content editors know HTML and ask for it to be amendable 🤦‍♂️

The beauty of Static HTML web part is that it can be easily personalized! Meaning that it is possible to change the URL of image displayed for different coutries, change the text a little or even sligly reorder items in the list.

So within Kentico MVC world we changed the approach to mainly hardcode these widgets markup into the page or widget MVC views. It goes with the couple of advantages:

  • The actual code is stored in the Visual Studio project and can be commited into the code repository.
  • Hardcoding is the fastest implementation.

And the disadvantages are:

  • Impossible to edit the hardcoded text ad-hoc.
  • As there are no properties in these hardcoded widgets - there is no way you can personalize them.

If the personalization is the key requirement - let's recreate Static HTML widget. The code can be found on the GitHub. All you need to do:

  1. Copy the code to your MVC solution and include files in the Visual Studio.
  2. Build and run the solution.
  3. Import static_html_page_types.zip via Kentico import. It contains a couple of page types I will cover later.

Reusable HTML chunks

The import package mentioned above contains Static HTML Container and Static HTML Chunk page types. With just one long text field for HTML helping to structure and store reusable HTML markup chunks in case there is a need to display it in a couple of different places.

The code package also contains dead simple Static HTML widget. Controller:

public class StaticHtmlWidgetController : WidgetController<StaticHtmlWidgetProperties>
{
    protected readonly IStaticHtmlChunkRepository mStaticHtmlChunkRepository;

    public StaticHtmlWidgetController(IStaticHtmlChunkRepository staticHtmlChunkRepository)
    {
        mStaticHtmlChunkRepository = staticHtmlChunkRepository;
    }

    protected StaticHtmlWidgetViewModel GetModel(StaticHtmlWidgetProperties properties)
    {
        var model = new StaticHtmlWidgetViewModel {Html = properties.Html};
        if (!string.IsNullOrWhiteSpace(model.Html)) return model;

        var selectedItem = properties.StaticHtmlChunks?.FirstOrDefault();
        if (selectedItem != null)
            model.Html = this.mStaticHtmlChunkRepository.GetByNodeGuid(selectedItem.NodeGuid).Html;

        return model;
    }

    public ActionResult Index()
    {
        var properties = GetProperties();
        return PartialView("Widgets/_StaticHtml", GetModel(properties));
    }
}

Properties and view model:

public class StaticHtmlWidgetProperties : IWidgetProperties
{
    [EditingComponent(TextAreaComponent.IDENTIFIER, Label = "Html", Order = 0)]
    public string Html { get; set; }

    [EditingComponent(PageSelector.IDENTIFIER, Label = "Html chunk", Order = 1)]
    public IList<PageSelectorItem> StaticHtmlChunks { get; set; }
}

public class StaticHtmlWidgetViewModel
{
    public string Html { get; set; }
}

View:

@model DancingGoat.Models.Widgets.StaticHtmlWidget.StaticHtmlWidgetViewModel

@Html.Raw(Model.Html)

And repository:

public interface IStaticHtmlChunkRepository : IRepository
{
    StaticHtmlChunk GetByNodeGuid(Guid nodeGuid);
}

public class StaticHtmlChunkRepository : IStaticHtmlChunkRepository
{
    private readonly string mCultureName;
    private readonly bool mLatestVersionEnabled;

    public StaticHtmlChunkRepository(string cultureName, bool latestVersionEnabled)
    {
        mCultureName = cultureName;
        mLatestVersionEnabled = latestVersionEnabled;
    }

    public StaticHtmlChunk GetByNodeGuid(Guid nodeGuid)
    {
        return StaticHtmlChunkProvider
            .GetStaticHtmlChunks()
            .LatestVersion(mLatestVersionEnabled)
            .Published(!mLatestVersionEnabled)
            .OnSite(SiteContext.CurrentSiteName)
            .Culture(mCultureName)
            .CombineWithDefaultCulture()
            .WhereEquals("NodeGUID", nodeGuid)
            .TopN(1)
            .FirstOrDefault();
    }
}

Where you can select HTML chunk from the tree or just paste some HTML code directly into the widget.

Future-proof

There are a few ideas to scaffold for the future based on the fact that Static HTML widgets can be inherited. If you create a widget inherited from Static HTML you can:

  • Give child widget a specific descriptive name, like 'Company history timeline'
  • Include the default hardcoded markup in the view and commit to the code repository
  • When you have time and budget to revisit these widgets and make them fully configurable you will already have Kentico widget structure so you would just make this widget "dynamic". And it will also not break the existing content on the website! 😜

Wrapping up

Yes, I know that the technique described here would be considered as a bad practice. But sometimes to create an initial momentum for the project and demonstrate quick marketing wins developers need to make step back. Happy coding to you all!