Overview

Custom Content Settings (BusinessObjectCustomSetting<T>)

Overview

Custom Settings are extra settings objects that sit alongside the site's Site Settings. They work the same way: a structured content type registered with a domain, with only one published record available at a time. The difference is that you can have as many as you need, each with its own class, editor and menu entry.

Use them when a module, add-on or feature needs its own configuration (an API key, a default page size, a header message) and you don't want to change the tenant's Site Settings class to hold it.

Custom Settings consist of:

Once created, register the section under System Settings -> Data Sections -> Custom Settings. Unlike Site Settings, nothing has to be referenced in Domain Management: the section is assigned to its domains directly.

Editors maintain the settings under Domain Settings -> Site Content. Once a domain has a Custom Settings section, Site Content becomes a submenu: Site Settings first, then one entry per Custom Settings section.

Site Settings, Custom Settings or module Attributes?

Use For Example
Site Settings Things that describe the whole site Company name, logo, address
Custom Settings Configuration for one feature, the same on every page of a domain A feed API key, a default page size, text shown with every content area
Module Attributes Values that change per placement of a module on a page The content of one content area

Usage

Custom Settings can be accessed from:

  • AdvantagePageTemplate
  • AdvantageModule
  • AdvantageModuleRewrite
  • AdvantageHandler
  • The API base classes (AdvantageApiControllerBase, AdvantageApiAuthorizeBase, AdvantageApiCorsPolicyProviderBase, AdvantageDelegatingHandlerBase)
  • AdvantageModuleEngine

Using the following method:

  • this.CustomSettings<T>() -> returns the object defined by the developer, or null until a record has been published for the current domain and language.

There is no dynamic CustomSettings() overload. Always use the typed call.

Example Class (BusinessObjectCustomSettings)

Settings for a Content Area module: optional text shown above and below every content area on the domain.

Example Object

 

Override Validate the same way as Site Settings to stop an editor publishing incomplete settings.

Validate before publish

using AdvantageCMS.Core.Admin.Enums;
using AdvantageCMS.Core.Admin.Event;

public override bool Validate(eCMSActions action, ActionArgs e, out eCMSEngineEventStatus status, out string message)
{
    status = eCMSEngineEventStatus.Success;
    message = string.Empty;

    if (action == eCMSActions.Publish && string.IsNullOrEmpty(HeaderText) && string.IsNullOrEmpty(FooterText))
        message += "Enter a header or footer text before publishing.<br/>";

    if (string.IsNullOrEmpty(message)) return true;

    status = eCMSEngineEventStatus.Exception;
    return false;
}

Control:ToolControl

CustomSettings UserControl: ToolControl<T>: Where T BusinessObjectCustomSetting

Overview

A custom user-control for entering the settings. It's an ordinary detail editor: fill the form in LoadDataFromObject and copy it back to MyObject in SaveDataToObject. The admin opens the domain's single record in it, so no list control is needed.

Note: Don't put <% %> code blocks in the editor's markup, not even <label for="<%= txtFoo.ClientID %>">. The tool control adds its toolbar to the control at runtime, and ASP.NET then throws "The Controls collection cannot be modified because the control contains code blocks". Use <asp:Label AssociatedControlID="txtFoo" /> for labels.

Example Custom Setting Control

ContentAreaSettingsControl.ascx

<%@ Control Language="C#" AutoEventWireup="True" Inherits="ON211.ContentAreaSettingsControl" CodeFile="ContentAreaSettingsControl.ascx.cs" %>

<fieldset>
    <legend>Content Area</legend>
    <div class="form-row">
        <asp:Label runat="server" AssociatedControlID="txtHeaderText" Text="Header Text" />
        <asp:TextBox runat="server" ID="txtHeaderText" MaxLength="500" />
    </div>
    <div class="form-row">
        <asp:Label runat="server" AssociatedControlID="txtFooterText" Text="Footer Text" />
        <asp:TextBox runat="server" ID="txtFooterText" MaxLength="500" />
    </div>
</fieldset>

ContentAreaSettingsControl.ascx.cs

using AdvantageCMS.Core.Admin.BaseClasses;
using AdvantageCMS.Core.Admin.Event;

namespace ON211
{
    public partial class ContentAreaSettingsControl : ToolControl<ContentAreaSettings>
    {
        protected override void LoadDataFromObject(ActionArgs e)
        {
            txtHeaderText.Text = MyObject.HeaderText;
            txtFooterText.Text = MyObject.FooterText;
        }

        protected override void SaveDataToObject()
        {
            MyObject.HeaderText = txtHeaderText.Text.Trim();
            MyObject.FooterText = txtFooterText.Text.Trim();
        }
    }
}

Using Custom Settings

In a module

Read the settings once and expose them as a property. CustomSettings<T>() is already cached, so the property is just for readability and null-safety.

ContentArea.ascx.cs

using System;
using AdvantageCMS.Core.Common.BaseClasses;

namespace ON211
{
    public partial class Modules_ContentArea : AdvantageModule
    {
        /// <summary>
        /// Domain-level header/footer text; null until the Content Area custom settings are published.
        /// </summary>
        protected ContentAreaSettings Settings => CustomSettings<ContentAreaSettings>();

        protected void Page_Load(object sender, EventArgs e)
        {
            litContent.Text = Attributes.GetString("Content");
        }
    }
}

ContentArea.ascx

<%@ Control Language="C#" AutoEventWireup="true" Inherits="ON211.Modules_ContentArea" CodeFile="ContentArea.ascx.cs" %>

<%-- Header/footer come from the Content Area custom settings (Domain Settings > Site Content > Content Area). --%>
<% if (!string.IsNullOrEmpty(Settings?.HeaderText)) { %>
    <div class="content-area-header"><%: Settings.HeaderText %></div>
<% } %>
    <asp:Literal ID="litContent" runat="server"></asp:Literal>
<% if (!string.IsNullOrEmpty(Settings?.FooterText)) { %>
    <div class="content-area-footer"><%: Settings.FooterText %></div>
<% } %>

In AdvantagePageTemplate

ContentAreaSettings settings = CustomSettings<ContentAreaSettings>();
if (!string.IsNullOrEmpty(settings?.HeaderText))
    litBanner.Text = HttpUtility.HtmlEncode(settings.HeaderText);

From ModuleEngine

ContentAreaSettings settings = moduleEngine.CustomSettings<ContentAreaSettings>();

  • Use <%: %> (HTML-encoded) for anything an editor typed. Use <%= %> only for trusted values such as numbers or CSPNonce.
  • Never write secrets (API keys, passwords) into the markup or a script block. Use them in code-behind or on the server only.

Registering The Section

  1. Open System Settings -> Data Sections -> Custom Settings and add a section.
  2. Display Name: the name editors see in the Site Content menu (for example Content Area).
  3. Object Name: choose the class (ON211.ContentAreaSettings). The list shows only classes that inherit BusinessObjectCustomSetting<T>.
  4. Custom Setting Control: choose the .ascx (~/ON211/ClientAdmin/ModuleSettings/ContentAreaSettingsControl.ascx).
  5. Tick the domains it applies to, then save.

Editors then open Domain Settings -> Site Content -> Content Area, fill in the form and publish.

Notes

  • Permissions: Custom Settings use the domain's Site Settings permissions. Anyone who can view, save or publish Site Content can do the same to every Custom Settings section. They have no Security Roles of their own and don't appear in Roles.
  • One record per class: each domain and language has one published record per class. Every module, handler or API that reads the class shares it.
  • Caching: results are cached per class, for the request and in the web cache. Publishing clears only that class's cache entry, so don't add your own cache.
  • Not a Site Setting: a BusinessObjectCustomSetting<T> class can't be registered as the domain's Site Settings. The Site Settings picker leaves these classes out, and the Custom Settings picker shows only them. Calling SiteSettings<T>() with a custom settings class is routed to the custom settings lookup, but prefer CustomSettings<T>().
  • Inherited properties: BusinessObjectCustomSetting<T> builds on BusinessObjectSiteSetting<T>, so IntelliSense also shows the site settings base properties (GoogleAnalyticsCode, SiteScripts, SocialLinks and so on). Ignore them in a custom settings class.
  • Configuration sync: the section syncs like any other section. The settings values themselves aren't part of configuration sync.
Back to Top Button
↑