Open Closed

Add IdentityUser Entity Extensions to Profile Management Screen #3517


0
AMacaulayAtETV created
  • ABP Framework version: v5.3.3
  • UI type: Blazor
  • DB provider: EF Core
  • Tiered (MVC) or Identity Server Separated (Angular): Tiered

I've extended the IdentityUser entity using the directions outlined here: https://docs.abp.io/en/abp/latest/Customizing-Application-Modules-Extending-Entities In my test project, this has worked to add a "MiddleName" field that is present as a column on the AbpUsers table. They also show up on the Users CRUD page and modals.

However, they are not present on the profile management page (the one you reach by selecting the user icon on the top of the page and selecting "My Profile"). I've attempted to override the Manage Profile tab component so I could add it myself, following the steps in this guide: https://docs.abp.io/en/abp/latest/UI/AspNetCore/Customization-User-Interface. However, trying to visit this page throws the following exception:

InvalidOperationException: The model item passed into the ViewDataDictionary is of type 'Volo.Abp.Account.Public.Web.Pages.Account.Components.ProfileManagementGroup.PersonalInfo.AccountProfilePersonalInfoManagementGroupViewComponent+PersonalInfoModel', but this ViewDataDictionary instance requires a model item of type 'TieredTestProject.Pages.Account.Components.ProfileManagementGroup.PersonalInfo.MyProfileManagementModel'.

With "MyProfileManagementModel" being the model of my overriding component. I can do what I'm looking for if I include the source code for the Account module, but I'd rather not go that route as I don't seem to be able to easily update the ABP version after that (Suite, for example, only updates the main projects, not any of the modules, and updating the ABP packages manually causes errors).

I would like to know how to do one of the following:

  • Override the Profile Management tab without any errors so I can add my extended properties.
  • Have the Profile Management redirect to my own custom page so I don't have to bother with overriding UI.
  • Easily upgrade the version of ABP when the account module is included as source code.

Thank you.


6 Answer(s)
  • 0
    liangshiwei created
    Support Team

    Hi,

    The profile manage UI is extensible, you can easy to add a new tab.

    For example:

    public class MyProfileManagementPageContributor : IProfileManagementPageContributor
    {
        public async Task ConfigureAsync(ProfileManagementPageCreationContext context)
        {
            context.Groups.Add(
                new ProfileManagementPageGroup(
                    "my-account-profile",
                    "My account profile",
                    typeof(MyPictureManagementGroupViewComponent)
                )
            );
        }
    }
    
    public class MyPictureManagementGroupViewComponent: AbpViewComponent
    {
        public virtual async Task<IViewComponentResult> InvokeAsync()
        {
            return View("~/Pages/<Your path>/Default.cshtml");
        }
    }
    
    &lt;h1&gt;Hello world&lt;/h1&gt;
    
    public class YourModule : AbpModule
    {
       public override void ConfigureServices(ServiceConfigurationContext context)
       {
           Configure<ProfileManagementPageOptions>(options =>
           {
               options.Contributors.Add(new MyProfileManagementPageContributor());
           });
           
            Configure<AbpBundlingOptions>(options =>
            {
    
                options.ScriptBundles
                    .Configure(typeof(Volo.Abp.Account.Public.Web.Pages.AccountManageModel).FullName,
                        configuration =>
                        {
                            configuration.AddFiles("/Pages/<Your path>/Default.js");
                        });
            });
       }
    }
    
  • 0
    AMacaulayAtETV created

    Oh, thank you. Is it possible to remove/hide the existing tab for profile info? I just want to make a replacement tab that shows all the built-in IdentityUser properties + the extended properties I've added, formatted the way I'd like (one of them, for example, is a middle name that should have an input field between the first and last names).

  • 0
    liangshiwei created
    Support Team

    Hi,

    Yes you can.

    public class MyProfileManagementPageContributor : IProfileManagementPageContributor
    {
        public async Task ConfigureAsync(ProfileManagementPageCreationContext context)
        {
            context.Groups.RemoveAll(x => x.Id == "Volo-Abp-Account-PersonalInfo");
        }
    }
    
  • 0
    AMacaulayAtETV created

    So, I've managed to create a new profile management tab using the module's original source code as a base. I've modified the JavaScript to send an UpdateProfileDto to the ProfileAppService with my test extended property ("MiddleName") inside the ExtraProperties dictionary. However, this property doesn't get saved.

    Looking at a project that uses the source code for the Account module (so I can debug it), I can see that though the UpdateProfileDto's ExtraProperties reaches the ProfileAppService's Update method (so my JavaScript works), the ExtraProperties do not get mapped to the user after the "input.MapExtraPropertiesTo(user)" method call. How do I set up the ExtraProperty so that it will actually get mapped?

  • 0
    liangshiwei created
    Support Team

    Hi,

    This is my test code,

    public class MyPictureManagementGroupViewComponent: AbpViewComponent
    {
        private readonly IProfileAppService _profileAppService;
    
        public MyPictureManagementGroupViewComponent(IProfileAppService profileAppService)
        {
            _profileAppService = profileAppService;
        }
    
        public virtual async Task<IViewComponentResult> InvokeAsync()
        {
            var profile = await _profileAppService.GetAsync();
            return View("~/Pages/Account/Default.cshtml", profile);
        }
    }
    
    @inject IHtmlLocalizer<AccountResource> L
    @inject ICurrentUser CurrentUser
    @using Microsoft.AspNetCore.Mvc.Localization
    @using Microsoft.AspNetCore.Mvc.TagHelpers
    @using Volo.Abp.Account.Localization
    @using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Button
    @using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form
    @using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Grid
    @using Volo.Abp.Data
    @using Volo.Abp.Users
    @model Volo.Abp.Account.ProfileDto
    
    <input id="CurrentUserId" value="@CurrentUser.GetId()" hidden/>
    
    <form method="post" id="MyPersonalSettingsForm">
    
        <abp-input asp-for="UserName"/>
    
        <abp-row>
            <abp-column size-md="_6">
                <abp-input asp-for="Name"/>
            </abp-column>
            <abp-column size-md="_6">
                <abp-input asp-for="Surname"/>
            </abp-column>
        </abp-row>
    
        <label>MiddleName</label>
        <input type="text" class="form-control" value="@(Model.GetProperty<string>("MiddleName"))" id="MiddleName"/>
        <abp-input asp-for="Email"/>
        <abp-input asp-for="PhoneNumber"/>
    
        <abp-button type="submit" button-type="Primary" text="@L["Submit"].Value"/>
    </form>
    
    (function ($) {
    
        $(function () {
            var l = abp.localization.getResource("AbpAccount");
    
            var _profileService = volo.abp.account.profile;
            
    
            $("#MyPersonalSettingsForm").submit(function (e) {
                e.preventDefault();
    
                if (!$("#MyPersonalSettingsForm").valid()) {
                    return false;
                }
    
                var input = $("#MyPersonalSettingsForm").serializeFormToObject();
    
                input.ExtraProperties = {
                    "MiddleName" :$("#MiddleName").val()
                }
    
                _profileService.update(input).then(function (result) {
                    abp.notify.success(l("PersonalSettingsSaved"));
                    
                });
            });
        });
    })(jQuery);
    
    
    public class TestModule: ...
    {
        private static readonly OneTimeRunner OneTimeRunner = new OneTimeRunner();
        
        public override void PostConfigureServices(ServiceConfigurationContext context)
        {
            OneTimeRunner.Run(() =>
            {
                ModuleExtensionConfigurationHelper.ApplyEntityConfigurationToApi(
                    IdentityModuleExtensionConsts.ModuleName,
                    IdentityModuleExtensionConsts.EntityNames.User,
                    getApiTypes: new[] { typeof(IdentityUserDto), typeof(ProfileDto) },
                    createApiTypes: new[] { typeof(IdentityUserCreateDto) },
                    updateApiTypes: new[] { typeof(IdentityUserUpdateDto) , typeof(UpdateProfileDto)}
                );
    
            });
        }
        
    
       .......
    }
    
  • 0
    AMacaulayAtETV created

    That last chunk of code is what I needed. Adding that to the module file for the IdentityServer project allowed the MiddleName to save.