V14: Create member filter (#15877)
* Update filter to include membergroup name * add filter by isApproved * Add isLockedOut * Implement member filter * Move filter logic to repository * Add more fields to sort by * Update openApi --------- Co-authored-by: Bjarke Berg <mail@bergmania.dk>
This commit is contained in:
@@ -6,6 +6,7 @@ using Umbraco.Cms.Api.Management.Factories;
|
||||
using Umbraco.Cms.Api.Management.ViewModels.Member;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Models.Membership;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
|
||||
namespace Umbraco.Cms.Api.Management.Controllers.Member.Filter;
|
||||
@@ -13,16 +14,13 @@ namespace Umbraco.Cms.Api.Management.Controllers.Member.Filter;
|
||||
[ApiVersion("1.0")]
|
||||
public class FilterMemberFilterController : MemberFilterControllerBase
|
||||
{
|
||||
private readonly IMemberTypeService _memberTypeService;
|
||||
private readonly IMemberService _memberService;
|
||||
private readonly IMemberPresentationFactory _memberPresentationFactory;
|
||||
|
||||
public FilterMemberFilterController(
|
||||
IMemberTypeService memberTypeService,
|
||||
IMemberService memberService,
|
||||
IMemberPresentationFactory memberPresentationFactory)
|
||||
{
|
||||
_memberTypeService = memberTypeService;
|
||||
_memberService = memberService;
|
||||
_memberPresentationFactory = memberPresentationFactory;
|
||||
}
|
||||
@@ -33,38 +31,30 @@ public class FilterMemberFilterController : MemberFilterControllerBase
|
||||
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status404NotFound)]
|
||||
public async Task<IActionResult> Filter(
|
||||
Guid? memberTypeId = null,
|
||||
string? memberGroupName = null,
|
||||
bool? isApproved = null,
|
||||
bool? isLockedOut = null,
|
||||
string orderBy = "username",
|
||||
Direction orderDirection = Direction.Ascending,
|
||||
string? filter = null,
|
||||
int skip = 0,
|
||||
int take = 100)
|
||||
{
|
||||
// TODO: Move to service once we have FilterAsync method for members
|
||||
string? memberTypeAlias = null;
|
||||
if (memberTypeId.HasValue)
|
||||
var memberFilter = new MemberFilter()
|
||||
{
|
||||
IMemberType? memberType = await _memberTypeService.GetAsync(memberTypeId.Value);
|
||||
if (memberType == null)
|
||||
{
|
||||
return MemberTypeNotFound();
|
||||
}
|
||||
MemberTypeId = memberTypeId,
|
||||
MemberGroupName = memberGroupName,
|
||||
IsApproved = isApproved,
|
||||
IsLockedOut = isLockedOut,
|
||||
Filter = filter,
|
||||
};
|
||||
|
||||
memberTypeAlias = memberType.Alias;
|
||||
}
|
||||
|
||||
IEnumerable<IMember> members = await Task.FromResult(_memberService.GetAll(
|
||||
skip,
|
||||
take,
|
||||
out var totalRecords,
|
||||
orderBy,
|
||||
orderDirection,
|
||||
memberTypeAlias,
|
||||
filter ?? string.Empty));
|
||||
PagedModel<IMember> members = await _memberService.FilterAsync(memberFilter, orderBy, orderDirection, skip, take);
|
||||
|
||||
var pageViewModel = new PagedViewModel<MemberResponseModel>
|
||||
{
|
||||
Items = await _memberPresentationFactory.CreateMultipleAsync(members),
|
||||
Total = totalRecords,
|
||||
Items = await _memberPresentationFactory.CreateMultipleAsync(members.Items),
|
||||
Total = members.Total,
|
||||
};
|
||||
|
||||
return Ok(pageViewModel);
|
||||
|
||||
@@ -16636,6 +16636,27 @@
|
||||
"format": "uuid"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "memberGroupName",
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "isApproved",
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "isLockedOut",
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "orderBy",
|
||||
"in": "query",
|
||||
|
||||
14
src/Umbraco.Core/Models/Membership/MemberFilter.cs
Normal file
14
src/Umbraco.Core/Models/Membership/MemberFilter.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
namespace Umbraco.Cms.Core.Models.Membership;
|
||||
|
||||
public class MemberFilter
|
||||
{
|
||||
public Guid? MemberTypeId { get; set; }
|
||||
|
||||
public string? MemberGroupName { get; set; }
|
||||
|
||||
public bool? IsApproved { get; set; }
|
||||
|
||||
public bool? IsLockedOut { get; set; }
|
||||
|
||||
public string? Filter { get; set; }
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Models.Membership;
|
||||
using Umbraco.Cms.Core.Persistence.Querying;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
|
||||
namespace Umbraco.Cms.Core.Persistence.Repositories;
|
||||
|
||||
@@ -38,4 +40,6 @@ public interface IMemberRepository : IContentRepository<int, IMember>
|
||||
/// <param name="query"></param>
|
||||
/// <returns></returns>
|
||||
int GetCountByQuery(IQuery<IMember>? query);
|
||||
|
||||
Task<PagedModel<IMember>> GetPagedByFilterAsync(MemberFilter memberFilter,int skip, int take, Ordering? ordering = null);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Models.Membership;
|
||||
using Umbraco.Cms.Core.Persistence.Querying;
|
||||
|
||||
namespace Umbraco.Cms.Core.Services;
|
||||
@@ -91,6 +92,13 @@ public interface IMemberService : IMembershipMemberService, IContentServiceBase<
|
||||
string? memberTypeAlias,
|
||||
string filter);
|
||||
|
||||
public Task<PagedModel<IMember>> FilterAsync(
|
||||
MemberFilter memberFilter,
|
||||
string orderBy = "username",
|
||||
Direction orderDirection = Direction.Ascending,
|
||||
int skip = 0,
|
||||
int take = 100);
|
||||
|
||||
/// <summary>
|
||||
/// Creates an <see cref="IMember" /> object without persisting it
|
||||
/// </summary>
|
||||
|
||||
@@ -99,6 +99,20 @@ namespace Umbraco.Cms.Core.Services
|
||||
|
||||
#region Create
|
||||
|
||||
public async Task<PagedModel<IMember>> FilterAsync(
|
||||
MemberFilter memberFilter,
|
||||
string orderBy = "username",
|
||||
Direction orderDirection = Direction.Ascending,
|
||||
int skip = 0,
|
||||
int take = 100)
|
||||
{
|
||||
using ICoreScope scope = ScopeProvider.CreateCoreScope(autoComplete: true);
|
||||
scope.ReadLock(Constants.Locks.MemberTypes);
|
||||
scope.ReadLock(Constants.Locks.MemberTree);
|
||||
|
||||
return await _memberRepository.GetPagedByFilterAsync(memberFilter, skip, take, Ordering.By(orderBy, orderDirection));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates an <see cref="IMember"/> object without persisting it
|
||||
/// </summary>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
using NPoco;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Core.Cache;
|
||||
@@ -202,6 +203,100 @@ public class MemberRepository : ContentRepositoryBase<int, IMember, MemberReposi
|
||||
return Database.ExecuteScalar<int>(fullSql);
|
||||
}
|
||||
|
||||
public async Task<PagedModel<IMember>> GetPagedByFilterAsync(MemberFilter memberFilter, int skip, int take, Ordering? ordering = null)
|
||||
{
|
||||
Sql<ISqlContext> sql = Sql().Select<NodeDto>(x => x.NodeId)
|
||||
.From<NodeDto>()
|
||||
.InnerJoin<MemberDto>().On<NodeDto, MemberDto>((n, m) => n.NodeId == m.NodeId);
|
||||
|
||||
if (memberFilter.MemberTypeId.HasValue)
|
||||
{
|
||||
sql = sql
|
||||
.InnerJoin<ContentDto>().On<NodeDto, ContentDto>((memberNode, memberContent) => memberContent.NodeId == memberNode.NodeId)
|
||||
.InnerJoin<NodeDto>("mtn").On<NodeDto, ContentDto>((memberTypeNode, memberContent) => memberContent.ContentTypeId == memberTypeNode.NodeId && memberTypeNode.UniqueId == memberFilter.MemberTypeId, "mtn");
|
||||
}
|
||||
|
||||
if (memberFilter.MemberGroupName.IsNullOrWhiteSpace() is false)
|
||||
{
|
||||
sql = sql
|
||||
.InnerJoin<Member2MemberGroupDto>().On<MemberDto, Member2MemberGroupDto>((m, memberToGroup) => m.NodeId == memberToGroup.Member)
|
||||
.InnerJoin<NodeDto>("mgn").On<NodeDto, Member2MemberGroupDto>((memberGroupNode, memberToGroup) => memberToGroup.MemberGroup == memberGroupNode.NodeId && memberGroupNode.Text == memberFilter.MemberGroupName, "mgn");
|
||||
}
|
||||
|
||||
if (memberFilter.IsApproved is not null)
|
||||
{
|
||||
sql = sql.Where<MemberDto>(member => member.IsApproved == memberFilter.IsApproved);
|
||||
}
|
||||
|
||||
if (memberFilter.IsLockedOut is not null)
|
||||
{
|
||||
sql = sql.Where<MemberDto>(member => member.IsLockedOut == memberFilter.IsLockedOut);
|
||||
}
|
||||
|
||||
if (memberFilter.Filter is not null)
|
||||
{
|
||||
var whereClauses = new List<Func<Sql<ISqlContext>, Sql<ISqlContext>>>()
|
||||
{
|
||||
(x) => x.Where<NodeDto>(memberNode => memberNode.Text != null && memberNode.Text.Contains(memberFilter.Filter)),
|
||||
(x) => x.Where<MemberDto>(memberNode => memberNode.Email.Contains(memberFilter.Filter)),
|
||||
(x) => x.Where<MemberDto>(memberNode => memberNode.LoginName.Contains(memberFilter.Filter)),
|
||||
};
|
||||
|
||||
if (int.TryParse(memberFilter.Filter, out int filterAsIntId))
|
||||
{
|
||||
whereClauses.Add((x) => x.Where<NodeDto>(memberNode => memberNode.NodeId == filterAsIntId));
|
||||
}
|
||||
|
||||
if (Guid.TryParse(memberFilter.Filter, out Guid filterAsGuid))
|
||||
{
|
||||
whereClauses.Add((x) => x.Where<NodeDto>(memberNode => memberNode.UniqueId == filterAsGuid));
|
||||
}
|
||||
|
||||
sql = sql.WhereAny(whereClauses.ToArray());
|
||||
}
|
||||
|
||||
if (ordering is not null)
|
||||
{
|
||||
ApplyOrdering(ref sql, ordering);
|
||||
}
|
||||
|
||||
var pageIndex = skip / take;
|
||||
Page<MemberDto>? pageResult = await Database.PageAsync<MemberDto>(pageIndex+1, take, sql);
|
||||
|
||||
// shortcut so our join is not too big, but we also hope these are cached, so we don't have to map them again.
|
||||
var nodeIds = pageResult.Items.Select(x => x.NodeId).ToArray();
|
||||
|
||||
return new PagedModel<IMember>(pageResult.TotalItems, nodeIds.Any() ? GetMany(nodeIds) : Array.Empty<IMember>());
|
||||
}
|
||||
|
||||
private void ApplyOrdering(ref Sql<ISqlContext> sql, Ordering ordering)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(sql);
|
||||
ArgumentNullException.ThrowIfNull(ordering);
|
||||
|
||||
if (ordering.OrderBy.IsNullOrWhiteSpace())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var orderBy = ordering.OrderBy.ToLowerInvariant() switch
|
||||
{
|
||||
"username" => sql.GetAliasedField(SqlSyntax.GetFieldName<MemberDto>(x => x.LoginName)),
|
||||
"name" => sql.GetAliasedField(SqlSyntax.GetFieldName<NodeDto>(x => x.Text)),
|
||||
"email" => sql.GetAliasedField(SqlSyntax.GetFieldName<MemberDto>(x => x.Email)),
|
||||
_ => throw new NotSupportedException("Ordering not supported"),
|
||||
};
|
||||
|
||||
if (ordering.Direction == Direction.Ascending)
|
||||
{
|
||||
sql.OrderBy(orderBy);
|
||||
}
|
||||
else
|
||||
{
|
||||
sql.OrderByDescending(orderBy);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets paged member results.
|
||||
/// </summary>
|
||||
|
||||
Reference in New Issue
Block a user