Take the community feedback survey now.

Tomek Juranek
Apr 6, 2025
  729
(3 votes)

Show/hide blocks and properties based on selected site in multi-site solution

Optimizely PaaS CMS has a great support for multi-site solutions which can additionally be extended with the custom code. For example, we can enhance it to show or hide entire blocks or blocks elements based on the selected site in a multi-site setup. This enables tailored content experiences across different domains while maintaining a single content structure.

First let's create enum with list of our sites, it should match site definition names in CMS Settings -> Manage Websites panel:

public enum MySite 
{
    Site1,
    Site2,
    Site3
}

We will also need a helper class to resolve a site based on current host name and port (helpful for local development):

public interface ISiteHelper
{
    public MySite GetCurrentSite(ExtendedMetadata metadata);
}

public class SiteHelper : ISiteHelper
{
    private readonly ISiteDefinitionResolver _siteDefinitionResolver;
    private readonly IHttpContextAccessor _httpContextAccessor;

    public SiteHelper(ISiteDefinitionResolver siteDefinitionResolver, IHttpContextAccessor httpContextAccessor)
    {
        _siteDefinitionResolver = siteDefinitionResolver;
        _httpContextAccessor = httpContextAccessor;
    }

    public MySite GetCurrentSite(ExtendedMetadata metadata)
    {
        dynamic contentMetadata = metadata;
        var ownerContent = contentMetadata?.OwnerContent as IContent;

        SiteDefinition siteDefinition = null;
        if (ownerContent != null && ownerContent?.ContentLink?.ID != 0)
        {
            siteDefinition = _siteDefinitionResolver.GetByContent(ownerContent?.ContentLink, true);
        }

        if (siteDefinition == null){
            var request = _httpContextAccessor.HttpContext?.Request;
            var host = request?.Host.Host;
            if (request.Host.Port != null)
            {
                host += ":" + request.Host.Port;
            }
            siteDefinition = _siteDefinitionResolver.GetByHostname(host, true);
        }

        if (siteDefinition != null)
        {
            foreach (var name in Enum.GetNames(typeof(MySite)))
           {
               if (siteDefinition.Name.Equals(name, StringComparison.InvariantCultureIgnoreCase))
               {
                   return (MySite)Enum.Parse(typeof(MySite), name);
               }
           }
        }
        throw new Exception($"Site not configured");
    }
}
 

Next we can implement an attribute which utilizes the helper class described above. Besides hiding the element, we want to ensure that if property is required but should be hidden validation logic won't be execured for that site:

public class ShowOnSitesAttribute : Attribute, IDisplayMetadataProvider
{
    private readonly MySite[] _sites;
    private readonly ISiteHelper _siteHelper;
    public bool Hide => !_sites.Contains(_siteHelper.GetCurrentSite());
    
    public ShowOnSitesAttribute(params MySite[] sites)
    {
        _sites = sites;
        _siteHelper = ServiceLocator.Current.GetService();
    }

    public void CreateDisplayMetadata(DisplayMetadataProviderContext context)
    {
         var extendedMetadata = context.DisplayMetadata.AdditionalValues[ExtendedMetadata.ExtendedMetadataDisplayKey] as ExtendedMetadata;
         if (_sites.Contains(_siteHelper.GetCurrentSite(extendedMetadata)))
        {
            return;
        }

        if (extendedMetadata == null)
        {
            return;
        }

        extendedMetadata.ShowForEdit = false;
        foreach (var property in extendedMetadata.Properties)
        {
            property.IsRequired = false;
            property.ValidationMetadata.IsRequired = false;
        }
    }
} 

Finally we can use the attribute in a block for entire class or for the properties:

[ContentType(DisplayName = "My Block", GUID = "A4ED27A1-2B34-4323-885E-4B478A137578")]
[ShowOnSites(MySite.Site1, MySite.Site2)]
public class MyBlock : BlockData
{
    [CultureSpecific]
    [Required]
    [Display(Name = "Heading", GroupName = TabNames.Content, Order = 10)]
    [ShowOnSites(MySite.Site1)]
    public virtual string Heading { get; set; }

    [CultureSpecific]
    [Display(Name = "Description", GroupName = TabNames.Content, Order = 20)]
    public virtual XhtmlString Description { get; set; }
} 
Apr 06, 2025

Comments

Bartosz Sekula
Bartosz Sekula Apr 22, 2025 02:49 PM

Have you considered scenarios where only a single host has the EDIT type?

Maybe instead of relying on the Request.Url you could try to identify the site based on the content item being edited?

So instead of IDisplayMetadataProvider you could try to use IMetadataAware

Tomek Juranek
Tomek Juranek May 16, 2025 11:40 AM

Thanks for your feedback, I updated the code to resolve site from content item.

Please login to comment.
Latest blogs
Jhoose Security Modules v2.5.0 – Import/Export Configs, .NET 9 Support and More

Discover what’s new in Jhoose Security Modules v2.5.0 — including import/export for configurations, Content Security Policy settings, and security...

Andrew Markham | Oct 9, 2025 |

Quiet Performance Wins: Scheduled Job for SQL Index Maintenance in Optimizely

As Optimizely CMS projects grow, it’s not uncommon to introduce custom tables—whether for integrations, caching, or specialized business logic. But...

Stanisław Szołkowski | Oct 8, 2025 |

Image Generation with Gemini 2.5 Flash

Gemini 2.5 Flash Image, nicknamed Nano Banana, is Google DeepMind’s newest image generation & editing model. It blends text‑to‑image, multi‑image...

Luc Gosso (MVP) | Oct 8, 2025 |

Automated Page Audit for Large Content Sites in Optimizely

Large content sites often face significant challenges in maintaining visibility and control over thousands of pages. Content managers struggle to...

Sanjay Kumar | Oct 6, 2025