Hi,
Do you use LeptonX theme?
And how you were using this scrollbar API in your application? That might be an internal API that you used in javascript
Hi,
I investigated how system works and found the following points.
The following method is called from everywhere whenever event name required: https://github.com/abpframework/abp/blob/97e10cc7b2b849b0aebf2aa00881616d036b0af1/framework/src/Volo.Abp.EventBus.Abstractions/Volo/Abp/EventBus/EventNameAttribute.cs#L22 Since this is a static method, it cannot be overriden in your project.
Another option seems like overriding RabbitMqDistributedEventBus in your project. Here is the original source code of that class:
https://github.com/abpframework/abp/blob/rel-9.1/framework/src/Volo.Abp.EventBus.RabbitMQ/Volo/Abp/EventBus/RabbitMq/RabbitMqDistributedEventBus.cs
using Microsoft.Extensions.Options;
using RabbitMQ.Client;
using Volo.Abp;
using Volo.Abp.DependencyInjection;
using Volo.Abp.EventBus;
using Volo.Abp.EventBus.Distributed;
using Volo.Abp.EventBus.Local;
using Volo.Abp.EventBus.RabbitMq;
using Volo.Abp.Guids;
using Volo.Abp.MultiTenancy;
using Volo.Abp.RabbitMQ;
using Volo.Abp.Timing;
using Volo.Abp.Tracing;
using Volo.Abp.Uow;
namespace MyCompanyName.MyApplicationName;
[Dependency(ReplaceServices = true)]
[ExposeServices(typeof(IDistributedEventBus), typeof(RabbitMqDistributedEventBus), typeof(IRabbitMqDistributedEventBus))]
public class MyRabbitMqDistributedEventBus : RabbitMqDistributedEventBus
{
    public MyRabbitMqDistributedEventBus(
        IOptions<AbpRabbitMqEventBusOptions> options,
        IConnectionPool connectionPool,
        IRabbitMqSerializer serializer,
        IServiceScopeFactory serviceScopeFactory,
        IOptions<AbpDistributedEventBusOptions> distributedEventBusOptions,
        IRabbitMqMessageConsumerFactory messageConsumerFactory,
        ICurrentTenant currentTenant,
        IUnitOfWorkManager unitOfWorkManager,
        IGuidGenerator guidGenerator,
        IClock clock,
        IEventHandlerInvoker eventHandlerInvoker,
        ILocalEventBus localEventBus,
        ICorrelationIdProvider correlationIdProvider) : base(options, connectionPool, serializer, serviceScopeFactory, distributedEventBusOptions, messageConsumerFactory, currentTenant, unitOfWorkManager, guidGenerator, clock, eventHandlerInvoker, localEventBus, correlationIdProvider)
    {
    }
    public override IDisposable Subscribe(Type eventType, IEventHandlerFactory factory)
    {
        var handlerFactories = GetOrCreateHandlerFactories(eventType);
        if (factory.IsInFactories(handlerFactories))
        {
            return NullDisposable.Instance;
        }
        handlerFactories.Add(factory);
        if (handlerFactories.Count == 1)
        {
            // Here we are registering the consumer for the first time.
            Consumer.BindAsync( "SomePrefix_" + EventNameAttribute.GetNameOrDefault(eventType));
        }
        return new EventHandlerFactoryUnregistrar(this, eventType, factory);
    }
    protected override Task PublishAsync(IModel channel, string eventName, byte[] body, Dictionary<string, object>? headersArguments = null, Guid? eventId = null, string? correlationId = null)
    {
        eventName += "SomePrefix_" + eventName;
        return base.PublishAsync(channel, eventName, body, headersArguments, eventId, correlationId);
    }
    private List<IEventHandlerFactory> GetOrCreateHandlerFactories(Type eventType)
    {
        return HandlerFactories.GetOrAdd(
            eventType,
            type =>
            {
                var eventName = EventNameAttribute.GetNameOrDefault(type);
                EventTypes.GetOrAdd(eventName, eventType);
                return new List<IEventHandlerFactory>();
            }
        );
    }
}
In that case, you'll override this service both consumer & publisher projects to make consistency.
Can you check your application logs? There should be log that indicates which action fails for authentication. Then we can take action accordingly
Maintaining yet another solution does not seem like a great idea to me. Nor is it great to add coupling from numerous solutions by adding a Domain.Shared reference to the management solution for the sake of localizing a page.
But as I understand, there is no other option. Well, actually one more - a trade-off option - could be to make API request to abp/application-localization of each solution (supposing that all of them will be running in the production system) and to create a client-side dictionary (dictionaries), then - to extract the display name by the given localization key...
Instead of maintaining another project, you can use a shared access data source, like a shared CDN folder for json localization files or a shared redis cache that all applications can access directly etc. It depends how much flkexibility you need
Create a file named account.js in the same folder alongside Account/Default.cshtml file.
Set the content:
$(function () {
  var $form = $('#tenant-switch-form');
  if ($form.length > 0) {
    $form.on('submit', function (e) {
      e.preventDefault();
      var $this = $(this);
      var formData = $this.serialize();
      var action = $this.attr('action') || window.location.pathname;
      var method = $this.attr('method') || 'post';
      $.ajax({
        url: action,
        type: method.toUpperCase(),
        data: formData,
        success: function (data, status, xhr) {
          // On success, reload the page
          window.location.reload();
        },
        error: function (xhr, status, error) {
          alert('Tenant switch failed: ' + (xhr.responseText || error));
        }
      });
    });
  }
});
Account/Default.cshtml    <script src="~/Themes/LeptonX/Layouts/Account/account.js"></script>
id="tenant-switch-form" to the tenant switch form. It should be looking like that: <form id="tenant-switch-form" method="post" asp-page="/Abp/MultiTenancy/TenantSwitchModal">
    <select name="Input.Name"
        class="auto-complete-select"
        <!-- 
        ...
        -->
Hi,
Layout feature was added by one of the community member with this PR: https://github.com/abpframework/abp/pull/20911
You can review that PR and find out how it can be customized in your application.
You can create a new Pages/Public/CmsKit/Pages/Index.cshtml in your project and resolve layout according to your own logic here:

This is the full content of that file that you can use as base: https://github.com/suhaib-mousa/abp/blob/dev/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/Public/CmsKit/Pages/Index.cshtml
CMS Kit has a feature for that. If you create a page the pages page, you'll see "Change Home Page Status" menu item there. That makes that page as home page and this page will be rendered as home page automatically.

But remember, CMS Kit applies your public website. If you created your solution With Public Website option, this affects your public website. If you installed all packages of CMS Kit into single application, so it'll affect your project directly
Hi,
Thanks for reporting it. We found a problem related to the filesystem in the docs module. The version is retrieved wrong (since there is no versionning in the filesystem)
Here the related changes done in the PR: https://github.com/abpframework/abp/pull/22891
You can apply similar change in your application until we ship it in a release
Hi,
It's located in in the Account layout, and it's coming from the theme itself. It seems you're useing LeptonX theme, you can override the following page:
Themes/LeptonX/Layouts/Account/Default.cshtml@using Microsoft.Extensions.Localization
@using Microsoft.Extensions.Options
@using Volo.Abp.AspNetCore.Mvc.UI.Theme.LeptonX
@using Volo.Abp.LeptonX.Shared.Localization;
@using Volo.Abp.Localization
@using Volo.Abp.AspNetCore.Mvc.UI.Components.LayoutHook
@using Volo.Abp.AspNetCore.Mvc.UI.Theme.LeptonX.Bundling
@using Volo.Abp.AspNetCore.Mvc.UI.Theming
@using Volo.Abp.AspNetCore.Mvc.UI.Widgets.Components.WidgetScripts
@using Volo.Abp.AspNetCore.Mvc.UI.Widgets.Components.WidgetStyles
@using Volo.Abp.Ui.Branding
@using Volo.Abp.AspNetCore.Mvc.AntiForgery
@using Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy.Localization
@using Volo.Abp.AspNetCore.MultiTenancy
@using Volo.Abp.MultiTenancy
@using Volo.Abp.AspNetCore.Mvc.UI.Theme.LeptonX.Themes.LeptonX.Components.Common.PageAlerts
@using Volo.Abp.AspNetCore.Mvc.UI.Theme.LeptonX.Themes.LeptonX.Components.SideMenu.Toolbar.LanguageSwitch
@using Microsoft.AspNetCore.Http.Extensions
@using Volo.Abp.Ui.LayoutHooks
@using Volo.Abp.LeptonX.Shared
@inject IAbpAntiForgeryManager AbpAntiForgeryManager
@inject IBrandingProvider BrandingProvider
@inject IOptions<LeptonXThemeOptions> LeptonXThemeOptions
@inject IOptions<LeptonXThemeMvcOptions> LeptonXThemeMvcOptions
@inject LeptonXStyleProvider LeptonXStyleProvider
@inject IStringLocalizer<AbpUiMultiTenancyResource> MultiTenancyStringLocalizer
@inject IStringLocalizer<LeptonXResource> L
@inject ITenantResolveResultAccessor TenantResolveResultAccessor
@inject IOptions<AbpMultiTenancyOptions> MultiTenancyOptions
@inject ICurrentTenant CurrentTenant
@inject ThemeLanguageInfoProvider ThemeLanguageInfoProvider
@inject Volo.Abp.AspNetCore.Mvc.UI.Layout.IPageLayout PageLayout
@inject IOptions<AbpThemingOptions> ThemingOptions
@{
    AbpAntiForgeryManager.SetCookie();
    var langDir = CultureHelper.IsRtl ? "rtl" : string.Empty;
    var title = $"{ViewBag.Title ?? PageLayout.Content.Title} | {BrandingProvider.AppName}".Trim('|', ' ');
    var languageInfo = await ThemeLanguageInfoProvider.GetLanguageSwitchViewComponentModel();
    var returnUrl = System.Net.WebUtility.UrlEncode(Context.Request.GetEncodedPathAndQuery());
    var logoUrl = BrandingProvider.LogoUrl == null ? null : "--lpx-logo: url(" + Url.Content(BrandingProvider.LogoUrl.EnsureStartsWith('/').EnsureStartsWith('~')) + ");";
    var logoReverseUrl = BrandingProvider.LogoReverseUrl == null ? null : "--lpx-logo: url(" + Url.Content(BrandingProvider.LogoReverseUrl.EnsureStartsWith('/').EnsureStartsWith('~')) + ");";
    var selectedStyle = await LeptonXStyleProvider.GetSelectedStyleAsync();
    var selectedStyleFileName = CultureHelper.IsRtl ? selectedStyle + ".rtl" : selectedStyle;
    var accountLayoutBackgroundStyle = LeptonXThemeMvcOptions.Value.AccountLayoutBackgroundStyle ?? $"background-image: url('{Url.Content($"~/LeptonX/images/login-pages/login-bg-img-{selectedStyle}.svg")}') !important;";
}
<!DOCTYPE html>
<html lang="@CultureInfo.CurrentCulture.Name" dir="@langDir">
<head>
    @await Component.InvokeLayoutHookAsync(LayoutHooks.Head.First, StandardLayouts.Account)
    @if (!ThemingOptions.Value.BaseUrl.IsNullOrWhiteSpace())
    {
        <base href="@ThemingOptions.Value.BaseUrl" />
    }
    <title>@title</title>
    <meta name="viewport" content="width=device-width,initial-scale=1.0" />
    <meta charset="UTF-8" />
    <meta name="description" content="@ViewBag.MetaDescription">
    @if (LeptonXThemeOptions.Value.Favicon != null)
    {
        <link rel="icon" href="@Url.Content(LeptonXThemeOptions.Value.Favicon.Url)" type="@LeptonXThemeOptions.Value.Favicon.Type">
    }
    <abp-style-bundle name="@LeptonXThemeBundles.Styles.Global" />
    <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" />
    @await Component.InvokeAsync(typeof(WidgetStylesViewComponent))
    @await RenderSectionAsync("styles", false)
    @await Component.InvokeLayoutHookAsync(LayoutHooks.Head.Last, StandardLayouts.Account)
    <style>
        .lpx-login-bg {
            @Html.Raw(accountLayoutBackgroundStyle)
        }
        :root .lpx-theme-light {
            @logoUrl
        }
        :root .lpx-theme-dark {
            @logoReverseUrl
        }
        :root .lpx-theme-dim {
            @logoReverseUrl
        }
    </style>
</head>
<body class="abp-account-layout lpx-theme-@selectedStyle">
    @await Component.InvokeLayoutHookAsync(LayoutHooks.Body.First, StandardLayouts.Account)
    <div class="container-fluid p-0" style="overflow-x: hidden">
        @await Component.InvokeLayoutHookAsync(LayoutHooks.PageContent.First, StandardLayouts.Account)
        <div class="lpx-login-area">
            <div class="lpx-login-bg">
                <div class="d-flex flex-column justify-content-center min-vh-100">
                    <div class="row">
                        <div class="col-xxl-5 col-lg-7 col-md-8 col-11 mx-auto position-relative py-4">
                            @if (BrandingProvider.LogoUrl.IsNullOrEmpty())
                            {
                                <div class="lpx-logo-container lpx-login-brand-text">
                                    <div class="lpx-brand-logo lpx-login-logo mx-auto"></div>
                                    <div class="lpx-brand-name lpx-login-name mx-auto">@BrandingProvider.AppName</div>
                                </div>
                            }
                            else
                            {
                                <div class="lpx-brand-logo lpx-login-logo mb-3 mx-auto"></div>
                            }
                            <div class="card mx-auto" style="max-width: 30rem;">
                                <div class="card-body p-3 p-sm-4">
                                    @if (languageInfo.Languages.Count > 1)
                                    {
                                        <div class="align-items-start d-flex justify-content-between mb-2">
                                            <h2 class="lpx-main-title lpx-login-title m-0 me-auto"> @PageLayout.Content.Title @* TODO: Find a better text here. *@</h2>
                                            <div class="dropdown btn-group ms-auto" aria-labelledby="languageDropdown">
                                                <button class="btn btn-sm btn-light dropdown-toggle" type="button" data-bs-toggle="dropdown" aria-expanded="false">
                                                    <i class="bi bi-translate me-1"></i> @languageInfo.CurrentLanguage.DisplayName
                                                </button>
                                                <ul class="dropdown-menu dropdown-menu-end" aria-labelledby="dropdownMenuButton1" style="max-height: 60vh; overflow-y: auto;">
                                                    @foreach (var language in languageInfo.Languages)
                                                    {
                                                        var twoLetterLanguageName = new CultureInfo(language.CultureName).TwoLetterISOLanguageName.ToUpperInvariant();
                                                        var url = Url.Content($"~/Abp/Languages/Switch?culture={language.CultureName}&uiCulture={language.UiCultureName}&returnUrl={returnUrl}");
                                                        <li>
                                                            <a href="@url" class="dropdown-item" data-lpx-language-option="@twoLetterLanguageName">@language.DisplayName / @twoLetterLanguageName</a>
                                                        </li>
                                                    }
                                                </ul>
                                            </div>
                                        </div>
                                        <hr/>
                                    }
                                    @await Component.InvokeAsync(typeof(PageAlertsViewComponent))
                                    @if (MultiTenancyOptions.Value.IsEnabled &&
                                         (TenantResolveResultAccessor.Result?.AppliedResolvers?.Contains(CookieTenantResolveContributor.ContributorName)
                                          == true ||
                                          TenantResolveResultAccessor.Result?.AppliedResolvers?.Contains(QueryStringTenantResolveContributor.ContributorName)
                                          == true))
                                    {
                                        <div>
                                            <div class="row">
                                                <div class="col">
                                                    <span style="font-size: .8em;"
                                                          class="text-uppercase text-muted">@MultiTenancyStringLocalizer["Tenant"]</span><br/>
                                                    <h6 class="m-0 d-inline-block">
                                                        @if (CurrentTenant.Id == null)
                                                        {
                                                            <span>
                                                                @MultiTenancyStringLocalizer["NotSelected"]
                                                            </span>
                                                        }
                                                        else
                                                        {
                                                            <strong>
                                                                @(CurrentTenant.Name ??
                                                                  CurrentTenant.Id.Value.ToString())
                                                            </strong>
                                                        }
                                                    </h6>
                                                </div>
                                                <div class="col-auto">
                                                    <a id="AbpTenantSwitchLink" href="javascript:;"
                                                       class="btn btn-sm btn-outline-primary">@MultiTenancyStringLocalizer["Switch"]</a>
                                                </div>
                                            </div>
                                        </div>
                                        <hr/>
                                    }
                                    @RenderBody()
                                </div>
                                @* @await Html.PartialAsync("~/Themes/LeptonX/Layouts/Account/_Footer.cshtml") *@
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </div>
        @await Component.InvokeLayoutHookAsync(LayoutHooks.PageContent.Last, StandardLayouts.Account)
    </div>
    <abp-script-bundle name="@LeptonXThemeBundles.Scripts.Global" />
    <script src="~/Abp/ApplicationLocalizationScript?cultureName=@CultureInfo.CurrentUICulture.Name"></script>
    <script type="text/javascript" src="~/Abp/ApplicationConfigurationScript"></script>
    <script type="text/javascript" src="~/Abp/ServiceProxyScript"></script>
    @await Component.InvokeAsync(typeof(WidgetScriptsViewComponent))
    @await RenderSectionAsync("scripts", false)
    @await Component.InvokeLayoutHookAsync(LayoutHooks.Body.Last, StandardLayouts.Account)
</body>
</html>
This was the original content of the Account layout in v4.1
You should find the following section:
<div class="row">
    <div class="col">
        <span style="font-size: .8em;"
              class="text-uppercase text-muted">@MultiTenancyStringLocalizer["Tenant"]</span><br/>
        <h6 class="m-0 d-inline-block">
            @if (CurrentTenant.Id == null)
            {
                <span>
                    @MultiTenancyStringLocalizer["NotSelected"]
                </span>
            }
            else
            {
                <strong>
                    @(CurrentTenant.Name ??
                      CurrentTenant.Id.Value.ToString())
                </strong>
            }
        </h6>
    </div>
    <div class="col-auto">
        <a id="AbpTenantSwitchLink" href="javascript:;"
           class="btn btn-sm btn-outline-primary">@MultiTenancyStringLocalizer["Switch"]</a>
    </div>
</div>
And update it with the following code:
<div class="row">
    <form method="post" asp-page="/Abp/MultiTenancy/TenantSwitchModal">
            <select name="Input.Name"
                class="auto-complete-select"
                data-autocomplete-api-url="/api/app/tenant-lookup"
                data-autocomplete-display-property="name"
                data-autocomplete-value-property="name"
                data-autocomplete-items-property="items"
                data-autocomplete-filter-param-name="filter"
                data-autocomplete-allow-clear="true"
                >
                @if (CurrentTenant.Id != null)
                {
                    @* Tenant resolvers works with name, not id. So value must be name. *@
                    <option value="@CurrentTenant.Name">@CurrentTenant.Name</option>
                }
    </select>
     <button type="submit"
                class="btn btn-sm btn-outline-primary mt-2 w-100">@MultiTenancyStringLocalizer["Switch"]    </button>
    </form>
</div>
This will look something like that:

Let's implement lookup endpoint, we cannot use Saas endpoints since they require authorization.
ITenantLookupAppService.cs in your Application.Contracts layer:public interface ITenantLookupAppService : IApplicationService
{
    Task<PagedResultDto<TenantLookupDto>> GetListAsync(TenantLookupPagedResultRequestDto input);
}
public class TenantLookupDto
{
    public Guid Id { get; set; }
    public string Name { get; set; }
}
public class TenantLookupPagedResultRequestDto : PagedAndSortedResultRequestDto
{
    public string? Filter { get; set; }
}
- Create `TenantLookupAppService.cs` in your **Application** layer:
```cs
public class TenantLookupAppService : ApplicationService, ITenantLookupAppService
{
    private readonly ITenantRepository _tenantRepository;
    public TenantLookupAppService(ITenantRepository tenantRepository)
    {
        _tenantRepository = tenantRepository;
    }
    public async Task<PagedResultDto<TenantLookupDto>> GetListAsync(TenantLookupPagedResultRequestDto input)
    {
        var tenants = await _tenantRepository.GetListAsync(skipCount: input.SkipCount, maxResultCount: input.MaxResultCount, filter: input.Filter);
        var count = await _tenantRepository.GetCountAsync(filter: input.Filter);
        return new PagedResultDto<TenantLookupDto>(count, tenants.Select(t => new TenantLookupDto { Id = t.Id, Name = t.Name }).ToList());
    }
}
As a note: Submitting directly to a SwitchTenantModal endpoint, returns the switchtenant modal when succeeded, you can prevent it by submiting form by javascript instead page-submit directly.
Hi,
Do you have any errors or logs? Can you share logs of ABP Suite after code-generation?
ABP Suite logs are located in the following path:
%USERPROFILE%\.abp\suite\logs
 
                                