Open Closed

Tenant-specific themes #5295


User avatar
0
cbogner85 created
  • ABP Framework version: v7.2.2
  • UI type: MVC
  • DB provider: EF Core

Hello,

I'm trying to implement customized themes for some tenants (they need their own branding) and hide the default themes for them. Therefore, I created new LeptonX themes and added them in WebModule:

options.Styles.Add("tenant1-light",
	new LeptonXThemeStyle(
	LocalizableString.Create<LeptonXResource>("Theme:light"), "bi bi-sun-fill"));
options.Styles.Add("tenant1-dark",
	new LeptonXThemeStyle(
	LocalizableString.Create<LeptonXResource>("Theme:dark"), "bi bi-moon-fill"));
options.Styles.Add("tenant2-light",
	new LeptonXThemeStyle(
	LocalizableString.Create<LeptonXResource>("Theme:light"), "bi bi-sun-fill"));
options.Styles.Add("tenant2-dark",
	new LeptonXThemeStyle(
	LocalizableString.Create<LeptonXResource>("Theme:dark"), "bi bi-moon-fill"));

Since I don't want users to see the customized themes of other tenants (and also not the default styles!), I have overriden Themes\LeptonX\Components\Common\GeneralSettings\Default.cshtml and Themes\LeptonX\Components\Common\MobileGeneralSettings\Default.cshtml to only display themes of the current tenant.

It works, but how can I change the default theme per tenant? Normally, DefaultStyle is set in WebModule:

options.DefaultStyle = LeptonXStyleNames.Light;

I need a tenant-specific DefaultStyle (e.g. "tenant1-light" for tenant1, "tenant2-light" for tenant2). With my current approach, when a tenant1 user opens the page for the first time, it displays the default light style (although this is not visible in the user's theme list) instead of the tenant1-light style . He can switch to tenant1-light manually and this is persisted, however, when he changes the language, it falls back to default light style.

Additionally, with my approach the "System" theme doesn't work, as it uses default light/dark instead of my customized ones.

Is there a better approach to implement tenant-specific themes?

Thanks in advance and best regards


6 Answer(s)
  • User Avatar
    0
    Anjali_Musmade created
    Support Team Support Team Member

    Hi,

    Can you please use themes bundles in your MVC project and replace it with the condition, as below https://docs.abp.io/en/commercial/6.0/themes/lepton-x/commercial/mvc

  • User Avatar
    0
    cbogner85 created

    Hello,

    I'm already using LeptonX and have imeplemented custom themes. What I'm trying to accomplish: enable different themes based on the current tenant (only visible to this specific tenant) and set a different DefaultStyle based on the current tenant. The documentation shows how to hide styles, but in WebModule we don't have access to the current tenant as it runs at application startup and not at every page request.

    Best regards

  • User Avatar
    0
    enisn created
    Support Team .NET Developer

    Hi, it's not provided by default and and LeptonXThemeOptions is not a tenant-specific option. You can implement something different to make tenant-specific styles.

    1. You can override the Application Layout. ( Check here )
    2. You can inject any service that you check style for tenant and place style according to your logic.

    There is a section in the layout like below

    <head>
    
        @await Component.InvokeLayoutHookAsync(LayoutHooks.Head.First, StandardLayouts.Application)
    
        <title>@title</title>
    
        <meta name="viewport" content="width=device-width,initial-scale=1.0" />
        <meta charset="UTF-8" />
        <meta name="description" content="@ViewBag.MetaDescription" />
    
        <link rel="icon" href="~/favicon.svg" type="image/svg+xml">
        
        <!-- 👇 You can customize this section in cshtml 👇 -->
        <link href="~/Themes/LeptonX/Global/side-menu/css/bootstrap-@(selectedStyleFileName).css" type="text/css" rel="stylesheet" id="lpx-theme-bootstrap-@selectedStyle" />
        <link href="~/Themes/LeptonX/Global/side-menu/css/@(selectedStyleFileName).css" type="text/css" rel="stylesheet" id="lpx-theme-color-@selectedStyle" />
    
        <abp-style-bundle name="@LeptonXThemeBundles.Styles.Global" />
    
    

    You can access the entire page code by downloading LeptonX source code

  • User Avatar
    0
    enisn created
    Support Team .NET Developer

    As a second approach, you can replace LeptonXStyleProvider implementation in your application instead customizing application layout.

    
    [Dependency(ReplaceServices = true)]
    [ExposeServices(typeof(LeptonXStyleProvider))]
    public class MyLeptonXStyleProvider : LeptonXStyleProvider
    {
        public ICurrentTenant CurrentTenant { get; }
    
        public MyLeptonXStyleProvider(
            IHttpContextAccessor httpContextAccessor, 
            IOptions<LeptonXThemeOptions> leptonXThemeOption,
            ICurrentTenant currentTenant) : base(httpContextAccessor, leptonXThemeOption)
        {
            CurrentTenant = currentTenant;
        }
    
        public override async Task<string> GetSelectedStyleAsync()
        {
            if (CurrentTenant.Name == "Volosoft")
            {
                return "blue"; // It'll load blue.css (don't use '.css' extension here)
            }
    
            return await base.GetSelectedStyleAsync();
        }
    }
    

    Make sure cookies are deleted, otherwise the client will load the last css after JS is initialized. To disable JavaScript style loading by overriding the Themes\LeptonX\Global\scripts\style-initializer.js file with the following customized version:

    (function () {
        window.initLeptonX = function (layout) {
            leptonx.init.run();
        }
    })();
    

    It can be placed under wwwroot

  • User Avatar
    0
    cbogner85 created

    Hi @ensin,

    thanks for your reply!

    Both approaches work almost as expected, except for the fact that, because of disabling JS style loading, switching themes doesn't work properly - nothing happens after clicking on a theme name.

    I worked around this by adding onclick="location.reload()" to the menu items:

    Is there a better solution?

    Thanks in advance and best regards

  • User Avatar
    0
    enisn created
    Support Team .NET Developer

    It seems working, If it meets your requirements that is acceptable.


    Bu if you don't want to customize the theme source code, you can use the original JS file and customize loading CSS logic.

    (function () {
        window.initLeptonX = function (layout = currentLayout) {
            currentLayout = layout;
            
            // 👇 This event will be called when user interacts a theme change button.
            leptonx.CSSLoadEvent.on(event => {
                // 👇 You can even customize here according to your requirements.
                loadThemeCSS('bootstrap', event, 'bootstrap-');
                loadThemeCSS('color', event, '');
            });
    
            leptonx.init.run();
        }
            function isAlreadyLoaded(id) {
            return document.querySelector(`link[id^="lpx-theme-${id}-"]`)?.id;
        }
      
        function loadThemeCSS(key, event, cssPrefix) {
            const newThemeId = createId(event.detail.theme, key);
            const previousThemeId = createId(event.detail.previousTheme, key);
            const loadedCSS = isAlreadyLoaded(key);
      
            if (newThemeId !== loadedCSS) {
                leptonx.replaceStyleWith(
                    createStyleUrl(cssPrefix + event.detail.theme),
                    newThemeId,
                    previousThemeId || loadedCSS
                );
            }
          }
    
        function createId(theme, type) {
            return theme && `lpx-theme-${type}-${theme}`;
        }
    
        leptonx.CSSLoadEvent.on(event => {
            loadThemeCSS('bootstrap', event, 'bootstrap-');
            loadThemeCSS('color', event, '');
        });
    
        function createStyleUrl(theme, type) {
    
            if (isRtl()) {
                theme = theme + '.rtl';
            }
            
            if (type) {
                return `/Themes/LeptonX/Global/${currentLayout}/css/${type}-${theme}.css`;
            }
            return `/Themes/LeptonX/Global/${currentLayout}/css/${theme}.css`;
        }
    
        function isRtl() {
            return document.documentElement.getAttribute('dir') === 'rtl';
        }
    })();
    
Made with ❤️ on ABP v9.1.0-preview. Updated on November 11, 2024, 11:11