Skip to Main content

Optimizely CMS: Site Settings

A plugin for Optimizely CMS that adds per-site settings management directly in the CMS editor UI. Define strongly-typed settings content types and retrieve them in code with full multi-language and multi-site support.

Features

  • Per-site settings - each site gets its own settings folder, created automatically when a site is added and retained when its display name changes.
  • Strongly-typed - define settings as C# classes with property editors, just like regular Optimizely content.
  • Multi-language - settings respect language branches and fallback chains configured in the CMS.
  • Edit & published mode - draft settings are available in edit mode, published settings in default mode.
  • CMS integration - settings appear in the assets pane navigation tree and are searchable via the global search.
  • Caching - settings are cached per site/type/language with synchronized invalidation on content and language changes.

Requirements

  • .NET 10+
  • Optimizely CMS 13.1+ (EPiServer.CMS.UI.Core >= 13.1.1)

Installation

Install from Microsoft NuGet or Optimizely Nuget:

dotnet add package TuyenPham.SiteSettings

Register the service in your Startup.cs or Program.cs:

using TuyenPham.SiteSettings.DependencyInjection;

services.AddSiteSettings();

The module initializes automatically via IConfigurableModule. No additional startup code is needed.

Usage

1. Define a settings content type

Create a class that inherits from SettingsBase and decorate it with [SettingsContentType]:

using TuyenPham.SiteSettings.Models;

[SettingsContentType(
    DisplayName = "General Settings",
    GUID = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx")]
public class GeneralSettings
    : SettingsBase
{
    [Display(
        Name = "Site Title",
        GroupName = SystemTabNames.Content,
        Order = 10)]
    public virtual string? SiteTitle { get; set; }

    [Display(
        Name = "Footer Text",
        GroupName = SystemTabNames.Content,
        Order = 20)]
    public virtual string? FooterText { get; set; }
}

A settings instance is automatically created for each site. During startup, the module also reconciles existing sites, so a newly added settings type receives an instance without manual content creation.

2. Retrieve settings in code

Inject ISettingsService and call GetSiteSettings<T>():

using TuyenPham.SiteSettings.Services;

public class MyController(
    ISettingsService settingsService)
     : Controller
{
    public IActionResult Index()
    {
        var settings = settingsService
        .GetSiteSettings<GeneralSettings>();

        // settings?.SiteTitle,
        // settings?.FooterText,
        // etc.
        return View(settings);
    }
}

Parameters

ParameterTypeDefaultDescription
siteIdstring?nullSite identifier. Resolved from the current HTTP context when null; explicit values are case-insensitive and normalized to the CMS application name. Blank or unknown values return no settings.
languagestring?nullLanguage branch. Uses the preferred culture when null.

3. Edit settings in the CMS

Settings appear under the Site Settings navigation component in the CMS assets pane. Editors can manage settings per site and per language, just like regular content.

How it works

  1. Initialization - On application startup, the module registers a content root named SettingsRoot under the CMS root page. It reconciles every site and every type decorated with [SettingsContentType], creating missing folders and settings content.

  2. Caching - Settings are cached in ISynchronizedObjectInstanceCache per site, content type, and language, with separate entries for published and draft modes. Draft saves invalidate only the local draft entry; published, language, and typed site lifecycle events synchronize invalidation across nodes. Cache fills use bounded lock stripes to avoid duplicate work without globally serializing requests. Missing settings for an existing site use a one-minute negative cache; unknown site IDs are never cached.

  3. Retrieval - GetSiteSettings<T>() reads from cache, resolving language fallback chains from IContentLanguageSettingsHandler. In edit mode, a common draft is returned when available; otherwise the published version is used. Default mode returns the published version.

  4. Site lifecycle - Typed CMS event subscribers automatically create and permanently remove settings folders when sites change. When a site is renamed, a default folder label follows the site name while an editor-defined label is preserved; its hidden site identifier is always updated.

Project structure

TuyenPham.SiteSettings/
├── Components/          # CMS navigation component
├── DependencyInjection/ # AddSiteSettings() extension method
├── Descriptors/         # Content repository descriptor
├── Infrastructure/      # CMS initialization module
├── Models/              # SettingsBase, SettingsContentTypeAttribute, SettingsFolder
├── Providers/           # Global search provider
├── Services/            # ISettingsService and SettingsService
└── ClientResources/     # Client-side assets (styles)

Running tests

The test project uses xUnit v3 and NSubstitute for mocking.

dotnet run --project TuyenPham.SiteSettings.Tests

Building the NuGet package

dotnet pack --configuration Release --output ./nupkg

License

Apache License, version 2.0