Open Closed

Filter User list by new extra property #784


User avatar
0
chofoza created
  • ABP Framework version: v4.1.0
  • UI type: MVC
  • DB provider: EF Core
  • Tiered (MVC) or Identity Server Seperated (Angular): no

Hi, I have added a new boolean field to the User object called Active. I have overwridden the Register page to set this to false for new users and it works.

I have also added a new menu item under User Management that goes to Identity/Users/Inactive. I've created the Inactive.cshtml/cs page for it, as well as Inactive.js to edit the Actions to only have Activate and Delete. The Activate function correctly sets the field to true and reloads the page, so most of what I want is working.

My problem: how do I filter the Users list that loads on the Inactive page? I have only been able to use the 'filter' option but that seems to only search on name/surname, and I can't get it to filter on another field.

That part of the js file is:

        var _$table = _$wrapper.find('table');
        var getInactiveFilter = function () {
            return {
                filter: 'test',
            };
        };
           abp.libs.datatables.normalizeConfiguration({
               order: [[1, 'asc']],
               processing: true,
               serverSide: true,
               scrollX: true,
               paging: true,
               ajax: abp.libs.datatables.createAjax(
                   _identityUserAppService.getList, getInactiveFilter
               ),
               columnDefs: abp.ui.extensions.tableColumns.get('identity.user').columns.toArray()
           })
       );

I have tried to make the getInactiveFilter function look like this (testing on a named field before attempting ExtraProperties):

var getInactiveFilter = function () {
            return {
                name: 'test',
            };
        };

That doesn't work at all - the parameter isn't even passed in the ajax request.

What I really want to do is filter the list in 2 ways (at once):

  1. only show users where Active=false
  2. only show users that are within the OU hierarchy of the current logged in user

The Inactive page is only visible to users within a certain role, and they will be approving new users that register within their OU structure. Can anyone help with the filtering?


5 Answer(s)
  • User Avatar
    0
    gterdem created
    Senior .NET Developer

    Hello @chofoza, <br>

    Module Extension Configuration (docs):

    Add configuration under ConfigureExtraProperties method in MyProjectName.Domain.Shared project as below: <br>

    ObjectExtensionManager.Instance.Modules()
        .ConfigureIdentity(identity =>
        {
            identity.ConfigureUser(user =>
            {
                user.AddOrUpdateProperty<bool>("Active");
            });
        });
    

    Overriding IdentityUserRepository:

    If we look at the source code of GetUserList, we need to override this behaviour. Create a new repository inhereted from IdentityUserRepository: <br>

    public class MyIdentityUserRepository : EfCoreIdentityUserRepository
    {
        public MyIdentityUserRepository(IDbContextProvider<IIdentityDbContext> dbContextProvider) : base(dbContextProvider)
        {
        }
    
        public override async Task<List<IdentityUser>> GetListAsync(string sorting = null,
            int maxResultCount = int.MaxValue, int skipCount = 0, string filter = null,
            bool includeDetails = false, CancellationToken cancellationToken = default){
            if (filter == "Active" || filter == "active")
            {
                var users = await this.GetDbSet()
                    .IncludeDetails(includeDetails)
                    .OrderBy(sorting ?? nameof(IdentityUser.UserName))
                    .PageBy(skipCount, maxResultCount)
                    .ToListAsync(GetCancellationToken(cancellationToken));
    
                return users.Where(q => (bool)q.GetProperty("Active")).ToList(); // Consider IdentityUserExtension
            }
            return await this.GetDbSet()
                .IncludeDetails(includeDetails)
                .WhereIf(
                    !filter.IsNullOrWhiteSpace(),
                    u =>                    u.UserName.Contains(filter) ||
                        u.Email.Contains(filter) ||
                        (u.Name != null && u.Name.Contains(filter)) ||
                        (u.Surname != null && u.Surname.Contains(filter)) ||
                        (u.PhoneNumber != null && u.PhoneNumber.Contains(filter))
                )            .OrderBy(sorting ?? nameof(IdentityUser.UserName))
                .PageBy(skipCount, maxResultCount)
                .ToListAsync(GetCancellationToken(cancellationToken));
        }
    }
    

    (bool)q.GetProperty("Active")can't be translated to query by EfCore, so it must be evaluated on client side.

    And result should be as below: <br>

    If you want to add property as seperate field to db table (docs)

    Create the mapping under EfCoreEntityExtensionMappings in MyProjectName.EntityFrameworkCore project as below: <br>

    ObjectExtensionManager.Instance
        .MapEfCoreProperty<IdentityUser, bool>("Active");
    

    IdentityUserExtensions (docs):

    <br> Also consider creating a file named IdentityUserExtensions under MyProject.Domain/Users folder and keep properties hard coded to avoid mistakes. <br>

    public static class IdentityUserExtensions
    {
        private const string ActivePropertyName = "Active";
    
        public static void SetActive(this IdentityUser user, bool isActive)    {
            user.SetProperty(ActivePropertyName, isActive);
        }
        public static bool GetActive(this IdentityUser user)
        {
            return user.GetProperty<bool>(ActivePropertyName);
        }
    }
    

    So you can use <br>

    return users.Where(q => q.GetActive()).ToList();
    

    instead of <br>

    return users.Where(q => (bool)q.GetProperty("Active")).ToList();
    
  • User Avatar
    0
    chofoza created

    Hi @gterdem.

    I got all of this working, but have been struggling with part 2 of my question. I am pretty sure I can get the linq query working to filter on the current users OU's (or start with GetUsersInOrganizationUnitWithChildrenAsync and then filter on Active only) but I can't work out how to get the current user in my EntityFrameworkCore project. I've tried various dependency injections but I keep getting errors. I also tried passing the user ID along with the Ajax but it's ignored.

    Can you please help?

    Also in my dev environment if I view the Inactive page and the normal Users page they both behave normally. However in production whichever one I view first is what I continue to see (ie if I look at Inactive first, I see the same filter and Actions when I go to Users, and vice versa). This only seems to be an issue in prod and I can't reproduce it in dev. Any ideas?

  • User Avatar
    0
    chofoza created

    Ok I have sorted out the filtering issue. I didn't understand how easy the dependency injection was but finally got my head round that. I am injecting the httpContextAssessor and the OU repository, and then finding the current users OU and calling the GetUsersInOrganizationUnitWithChildrenAsync method with its Code.

    The other problem I mentioned above is still happening: In dev if I look at my Inactive page and my normal Users page, they behave as expected. In live, whichever one I look at first becomes the "default" and I can't see the other one. The screens are very similar obviously, but the filter is fixed to the first one I chose, and the Actions list is fixed to the first one too. Something is being cached somewhere it shouldn't but I can't figure out why this only affects live and the dev behaviour is perfect?

  • User Avatar
    0
    gterdem created
    Senior .NET Developer

    Ok I have sorted out the filtering issue. I didn't understand how easy the dependency injection was but finally got my head round that. I am injecting the httpContextAssessor and the OU repository, and then finding the current users OU and calling the GetUsersInOrganizationUnitWithChildrenAsync method with its Code.

    I would suggest injecting ICurrentUser but ok if it's working for you.

    The other problem I mentioned above is still happening: In dev if I look at my Inactive page and my normal Users page, they behave as expected. In live, whichever one I look at first becomes the "default" and I can't see the other one. The screens are very similar obviously, but the filter is fixed to the first one I chose, and the Actions list is fixed to the first one too. Something is being cached somewhere it shouldn't but I can't figure out why this only affects live and the dev behaviour is perfect?

    I can't say that I understand this clearly and it seems this is not related with filtering user with an extra property. Could you please create a new issue related with it and supply a way that we can understand better? Something like a public user-pass if on production. Or a screen cast video record link etc.

  • User Avatar
    0
    ServiceBot created
    Support Team Automatic process manager

    This question has been automatically marked as stale because it has not had recent activity.

Made with ❤️ on ABP v9.1.0-preview. Updated on December 12, 2024, 07:15