Activities of "maliming"

Hi,

In WebAssembly mode the request goes through your Blazor project, and that forwarding endpoint loses the connection string, so the HttpApi.Host returns 415.

You can fix it in your Blazor project for now. Add this class:

using System.Linq;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.ApplicationModels;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.Extensions.Options;
using Volo.Abp.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc.Conventions;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Http.Client.ClientProxying;
using Volo.Abp.Http.Client.StaticProxying;
using Volo.Abp.Http.Client.Web.Conventions;
using Volo.Abp.Http.ProxyScripting.Generators;

[DisableConventionalRegistration]
public class MyHttpClientProxyServiceConvention : AbpHttpClientProxyServiceConvention
{
    public MyHttpClientProxyServiceConvention(
        IOptions<AbpAspNetCoreMvcOptions> options,
        IConventionalRouteBuilder conventionalRouteBuilder,
        IClientProxyApiDescriptionFinder clientProxyApiDescriptionFinder,
        IOptions<AbpHttpClientStaticProxyingOptions> staticProxyingOptions)
        : base(options, conventionalRouteBuilder, clientProxyApiDescriptionFinder, staticProxyingOptions)
    {
    }

    protected override void ConfigureParameters(ControllerModel controller)
    {
        if (AbpHttpClientProxyHelper.IsClientProxyService(controller.ControllerType))
        {
            foreach (var action in controller.Actions)
            {
                var actionApiDescription = FindActionApiDescriptionModel(controller, action);
                if (actionApiDescription == null)
                {
                    continue;
                }

                foreach (var parameter in action.Parameters.Where(p => p.BindingInfo == null))
                {
                    if (actionApiDescription.Parameters.Any(p =>
                            p.NameOnMethod == parameter.ParameterName &&
                            p.BindingSourceId == ParameterBindingSources.Body))
                    {
                        parameter.BindingInfo = BindingInfo.GetBindingInfo(new[] { new FromBodyAttribute() });
                    }
                }
            }
        }

        base.ConfigureParameters(controller);
    }
}

Then register it in ConfigureServices of your ...BlazorModule:

context.Services.Replace(ServiceDescriptor.Transient<IAbpServiceConvention, MyHttpClientProxyServiceConvention>());

This needs using Microsoft.Extensions.DependencyInjection.Extensions; and using Volo.Abp.AspNetCore.Mvc.Conventions;.

We'll fix this in the next patch version. Your ticket has been refunded.

Thanks

Hi,

The MudBlazor layout misses the theme that LeptonX loads on the first visit, so MudBlazor keeps its light palette.

You can fix it for now by adding this file to your Blazor project as wwwroot/leptonx-mudblazor-theme-sync.js:

(function () {
    var bridge = window.abpMudBlazorLeptonX;
    if (!bridge) {
        return;
    }

    bridge.registerThemeCallback = function (dotNetRef) {
        bridge._dotNetRef = dotNetRef;

        if (typeof leptonx !== 'undefined' && leptonx.CSSLoadEvent) {
            leptonx.CSSLoadEvent.on(function (event) {
                if (bridge._dotNetRef) {
                    bridge._dotNetRef.invokeMethodAsync('OnLeptonXThemeChanged', event.detail.theme === 'dark');
                }
            });

            dotNetRef.invokeMethodAsync('OnLeptonXThemeChanged', document.body.getAttribute('data-bs-theme') === 'dark');
        }
    };
})();

Then add it to both script bundles. In ConfigureBundles of your ...BlazorModule (server mode):

options.ScriptBundles.Configure(
    BlazorMudBlazorLeptonXThemeBundles.Scripts.Global,
    bundle =>
    {
        bundle.AddFiles("/leptonx-mudblazor-theme-sync.js");
    }
);

And in your ...ScriptBundleContributor (WebAssembly mode):

public override void ConfigureBundle(BundleConfigurationContext context)
{
    context.Files.AddIfNotContains("/leptonx-mudblazor-theme-sync.js");
}

AddIfNotContains needs using System.Collections.Generic;.

We'll fix this in the next patch version. Your ticket has been refunded.

Thanks

Hi,

AccountIdentityUserCreatedEventHandler doesn't start a unit of work, so it fails when the event comes from RabbitMQ.

You can use this workaround for now. Add this class to your Application project:

using System.Threading.Tasks;
using Volo.Abp.Account.Emailing;
using Volo.Abp.EventBus.Distributed;
using Volo.Abp.Identity;
using Volo.Abp.Settings;
using Volo.Abp.Uow;

namespace YourProjectName.Account;

public class MyAccountIdentityUserCreatedEventHandler :
    AccountIdentityUserCreatedEventHandler,
    IDistributedEventHandler<IdentityUserCreatedEto>
{
    public MyAccountIdentityUserCreatedEventHandler(
        IdentityUserManager userManager,
        IAccountEmailer accountEmailer,
        ISettingProvider settingProvider)
        : base(userManager, accountEmailer, settingProvider)
    {
    }

    [UnitOfWork]
    public new virtual Task HandleEventAsync(IdentityUserCreatedEto eventData)
    {
        return base.HandleEventAsync(eventData);
    }
}

Then remove the original handler in the ConfigureServices method of your Application module, so the email isn't sent twice:

using Volo.Abp.Account.Emailing;
using Volo.Abp.EventBus.Distributed;

Configure<AbpDistributedEventBusOptions>(options =>
{
    options.Handlers.Remove<AccountIdentityUserCreatedEventHandler>();
});

The messages that RabbitMQ is still retrying will also be handled after this change.

We'll fix this in the next patch release. Your ticket has been refunded.

Thanks

Hi,

After a forced password change, the change password page signs the user in directly and skips the email confirmation check that the normal login does.

You can use this workaround for now. Add this class to your AuthServer project:

using System.Threading.Tasks;
using Volo.Abp.Account.Public.Web.Pages.Account;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Identity;
using Volo.Abp.Security.Claims;

namespace YourProjectName.Pages.Account;

[Dependency(ReplaceServices = true)]
[ExposeServices(typeof(ChangePasswordModel))]
public class MyChangePasswordModel : ChangePasswordModel
{
    public MyChangePasswordModel(ICurrentPrincipalAccessor currentPrincipalAccessor)
        : base(currentPrincipalAccessor)
    {
    }

    protected override async Task<bool> ShouldRequireForcedTwoFactorConfirmationAsync(IdentityUser user)
    {
        return !await SignInManager.CanSignInAsync(user)
            || await base.ShouldRequireForcedTwoFactorConfirmationAsync(user);
    }
}

It checks the email confirmation before the user is signed in, so a user who hasn't confirmed their email is sent to the confirmation page after changing the password.

We'll fix this in the next patch release. Your ticket has been refunded.

Thanks

Hi,

ChatClientChat still uses AutoGrow, which was removed in MudBlazor 9. That's why the field renders as a single-line input.

You can use this workaround for now. It maps the old AutoGrow attribute to Sizing="InputSizing.Auto". Add this class to your Blazor.Client project:

using System.Threading.Tasks;
using Microsoft.AspNetCore.Components;
using MudBlazor;

namespace YourProjectName.Blazor.Client.Components;

public class AutoGrowMudTextField : MudTextField<string>
{
    public override Task SetParametersAsync(ParameterView parameters)
    {
        if (parameters.TryGetValue<object>("AutoGrow", out var autoGrow))
        {
            Sizing = autoGrow is true or "true" ? InputSizing.Auto : InputSizing.Fixed;
        }

        return base.SetParametersAsync(parameters);
    }
}

Then register it in the ConfigureServices method of both your Blazor and Blazor.Client project modules:

using YourProjectName.Blazor.Client.Components;

context.Services.AddTransient<global::MudBlazor.MudTextField<string>, AutoGrowMudTextField>();

Only text fields that still set AutoGrow to true are changed. You can remove this after upgrading to the version that includes the fix.

We'll fix this in the next patch release. Your ticket has been refunded.

Thanks

Hi,

Without the Searching permission, a user can only send messages to people they already have a conversation with. They can't start a new conversation, so the server rejects that message.

The Blazor chat page doesn't handle this correctly. The search still lists all users, and the page doesn't catch the error when sending fails. This is a bug on our side.

You can use this workaround for now. Add this class to your Application project. With it, users without the Searching permission only see the people they already have a conversation with in the search results.

using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Volo.Abp.DependencyInjection;
using Volo.Abp.PermissionManagement;
using Volo.Chat.Authorization;
using Volo.Chat.Conversations;
using Volo.Chat.Users;

namespace YourProjectName.Chat;

[Dependency(ReplaceServices = true)]
[ExposeServices(typeof(IContactAppService), typeof(ContactAppService))]
public class MyContactAppService : ContactAppService
{
    public MyContactAppService(
        IChatUserLookupService chatUserLookupService,
        IConversationRepository conversationRepository,
        IPermissionFinder permissionFinder)
        : base(chatUserLookupService, conversationRepository, permissionFinder)
    {
    }

    public override async Task<List<ChatContactDto>> GetContactsAsync(GetContactsInput input)
    {
        if (input.IncludeOtherContacts && !await AuthorizationService.IsGrantedAsync(ChatPermissions.Searching))
        {
            input.IncludeOtherContacts = false;
        }

        return await base.GetContactsAsync(input);
    }
}

We'll fix this in the next patch release. The search box will be hidden for users without the Searching permission, and a failed send will show an error message instead of breaking the page. Your ticket has been refunded.

Thanks

Hi,

The chat page reads the chat settings when it loads, but that endpoint requires the Chat.SettingManagement permission.

You can use this workaround for now. Add this class to your Application project. It allows users with either the Chat.Messaging or the Chat.SettingManagement permission to read the settings. Updating the settings still requires Chat.SettingManagement.

using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Volo.Abp.Authorization;
using Volo.Abp.DependencyInjection;
using Volo.Abp.SettingManagement;
using Volo.Chat.Authorization;
using Volo.Chat.Settings;

namespace YourProjectName.Chat;

[Dependency(ReplaceServices = true)]
[ExposeServices(typeof(ISettingsAppService), typeof(SettingsAppService))]
public class MyChatSettingsAppService : SettingsAppService, ISettingsAppService
{
    public MyChatSettingsAppService(ISettingManager settingManager)
        : base(settingManager)
    {
    }

    [AllowAnonymous]
    public new async Task<ChatSettingsDto> GetAsync()
    {
        if (!await AuthorizationService.IsGrantedAnyAsync(ChatPermissions.Messaging, ChatPermissions.SettingManagement))
        {
            throw new AbpAuthorizationException();
        }

        return await base.GetAsync();
    }
}

After that, users with only the Messaging permission can open the chat page.

We'll fix this in the next patch release. Your ticket has been refunded.

Thanks

Hi,

The side menu filter hides menu items on the client side, but after navigation the menu re-renders the previously selected item and that item loses its hidden state.

You can use this workaround for now. Add this class to your Blazor.Client project:

using System.Threading.Tasks;
using Microsoft.AspNetCore.Components;
using Microsoft.JSInterop;
using Volo.Abp.AspNetCore.Components.Web.MudBlazorLeptonXTheme.Components.ApplicationLayout.SideMenu.Navigation;
using Volo.Abp.DependencyInjection;

namespace YourProjectName.Blazor.Client.Components;

[ExposeServices(typeof(MainMenu))]
[Dependency(ReplaceServices = true)]
public class MyMainMenu : MainMenu
{
    [Inject]
    protected IJSRuntime JSRuntime { get; set; } = default!;

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        await base.OnAfterRenderAsync(firstRender);

        if (Menu?.IsFiltering == true)
        {
            try
            {
                await JSRuntime.InvokeVoidAsync("leptonx.sideMenu.filter.onInput");
            }
            catch (JSDisconnectedException)
            {
            }
            catch (JSException)
            {
                // The layout initializes the side menu script a moment after the first render.
            }
        }
    }
}

Then register it in the ConfigureServices method of your Blazor project module as well, so it is also used when the page runs in server mode:

using Microsoft.Extensions.DependencyInjection.Extensions;
using Volo.Abp.AspNetCore.Components.Web.MudBlazorLeptonXTheme.Components.ApplicationLayout.SideMenu.Navigation;
using YourProjectName.Blazor.Client.Components;

context.Services.Replace(ServiceDescriptor.Transient<MainMenu, MyMainMenu>());

We'll fix this in the next patch release. Your ticket has been refunded.

Thanks

Hi,

You can create the invitation and accept it in the same code. The user joins the tenant with the assigned roles right away. No email is sent and the user doesn't need to do anything.

Here is the template's SaasDataSeedContributor with this added:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Volo.Abp.Data;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Identity.UserInvitations;
using Volo.Abp.Identity.UserSharing;
using Volo.Abp.MultiTenancy;
using Volo.Abp.Uow;
using Volo.Saas.Editions;

namespace YourProjectName.Saas;

public class SaasDataSeedContributor : IDataSeedContributor, ITransientDependency
{
    // Host user email -> role names to assign in each tenant
    private static readonly Dictionary<string, List<string>> SharedUsers = new()
    {
        { "user1@yourcompany.com", new List<string> { "admin", "Manager" } },
        { "user2@yourcompany.com", new List<string> { "Manager" } }
    };

    private readonly IEditionDataSeeder _editionDataSeeder;
    private readonly ICurrentTenant _currentTenant;
    private readonly UserSharingManager _userSharingManager;
    private readonly UserInvitationManager _userInvitationManager;

    public SaasDataSeedContributor(
        IEditionDataSeeder editionDataSeeder,
        ICurrentTenant currentTenant,
        UserSharingManager userSharingManager,
        UserInvitationManager userInvitationManager)
    {
        _editionDataSeeder = editionDataSeeder;
        _currentTenant = currentTenant;
        _userSharingManager = userSharingManager;
        _userInvitationManager = userInvitationManager;
    }

    [UnitOfWork]
    public virtual async Task SeedAsync(DataSeedContext context)
    {
        using (_currentTenant.Change(context?.TenantId))
        {
            await _editionDataSeeder.CreateStandardEditionsAsync();
        }

        if (context?.TenantId != null)
        {
            await AddSharedUsersToTenantAsync(context.TenantId.Value);
        }
    }

    private async Task AddSharedUsersToTenantAsync(Guid tenantId)
    {
        using (_currentTenant.Change(null))
        {
            foreach (var (email, roles) in SharedUsers)
            {
                var hostUsers = await _userSharingManager.GetUsersByEmailFromHostAsync(email, includeLeaved: true);
                var hostUser = hostUsers.FirstOrDefault(u => u.TenantId == null);
                if (hostUser == null || hostUsers.Any(u => u.TenantId == tenantId))
                {
                    continue;
                }

                var invitation = await _userInvitationManager.CreateAsync(tenantId, email, roles);
                await _userSharingManager.AcceptInviteAsync(invitation.Invitation.Id, hostUser.UserName, null, hostUser.PasswordHash);
            }
        }
    }
}

The DbMigrator runs this for each tenant. A user who has joined the tenant before is skipped, even if they left it later, so running the migrator again doesn't add anyone twice.

  • On MySQL you need 10.6.1 or later. 10.6.0 doesn't map the invitation roles column for MySQL.
  • The roles must already exist in each tenant, otherwise the migration fails.
  • Remove this code after the migration. The data seed also runs when a new tenant is created, but the roles don't exist yet at that point, so it only logs a Role ... does not exist! error.

Thanks

Great

Showing 31 to 40 of 12919 entries
Boost Your Development
ABP Live Training
Packages
See Trainings
Mastering ABP Framework Book
The Official Guide
Mastering
ABP Framework
Learn More
Mastering ABP Framework Book
Made with ❤️ on ABP v10.8.0-preview. Updated on October 30, 2026, 06:34
1
ABP Assistant
🔐 You need to be logged in to use the chatbot. Please log in first.