2020-09-18 15:27:38 +02:00
using Microsoft.Extensions.Logging ;
2021-02-09 10:22:42 +01:00
using Umbraco.Cms.Core.Events ;
using Umbraco.Cms.Core.Models ;
using Umbraco.Cms.Core.Models.Membership ;
2021-05-11 14:33:49 +02:00
using Umbraco.Cms.Core.Notifications ;
2022-01-13 23:46:21 +00:00
using Umbraco.Cms.Core.Persistence ;
2021-02-09 10:22:42 +01:00
using Umbraco.Cms.Core.Persistence.Querying ;
using Umbraco.Cms.Core.Persistence.Repositories ;
2021-02-15 11:41:12 +01:00
using Umbraco.Cms.Core.Scoping ;
2021-02-09 11:26:22 +01:00
using Umbraco.Extensions ;
2017-12-28 09:18:09 +01:00
2022-01-14 10:57:31 +00:00
namespace Umbraco.Cms.Core.Services
2017-12-28 09:18:09 +01:00
{
/// <summary>
/// Represents the MemberService.
/// </summary>
2021-03-07 09:53:25 +01:00
public class MemberService : RepositoryService , IMemberService
2017-12-28 09:18:09 +01:00
{
private readonly IMemberRepository _memberRepository ;
private readonly IMemberTypeRepository _memberTypeRepository ;
private readonly IMemberGroupRepository _memberGroupRepository ;
private readonly IAuditRepository _auditRepository ;
private readonly IMemberGroupService _memberGroupService ;
2024-04-11 13:53:34 +02:00
private readonly Lazy < IIdKeyMap > _idKeyMap ;
2017-12-28 09:18:09 +01:00
#region Constructor
2022-06-07 15:28:38 +02:00
public MemberService (
ICoreScopeProvider provider ,
ILoggerFactory loggerFactory ,
IEventMessagesFactory eventMessagesFactory ,
IMemberGroupService memberGroupService ,
IMemberRepository memberRepository ,
IMemberTypeRepository memberTypeRepository ,
IMemberGroupRepository memberGroupRepository ,
2024-04-11 13:53:34 +02:00
IAuditRepository auditRepository ,
Lazy < IIdKeyMap > idKeyMap )
2020-09-18 15:27:38 +02:00
: base ( provider , loggerFactory , eventMessagesFactory )
2017-12-28 09:18:09 +01:00
{
_memberRepository = memberRepository ;
_memberTypeRepository = memberTypeRepository ;
_memberGroupRepository = memberGroupRepository ;
_auditRepository = auditRepository ;
2024-04-11 13:53:34 +02:00
_idKeyMap = idKeyMap ;
2017-12-28 09:18:09 +01:00
_memberGroupService = memberGroupService ? ? throw new ArgumentNullException ( nameof ( memberGroupService ) ) ;
}
#endregion
#region Count
/// <summary>
/// Gets the total number of Members based on the count type
/// </summary>
/// <remarks>
/// The way the Online count is done is the same way that it is done in the MS SqlMembershipProvider - We query for any members
/// that have their last active date within the Membership.UserIsOnlineTimeWindow (which is in minutes). It isn't exact science
/// but that is how MS have made theirs so we'll follow that principal.
/// </remarks>
/// <param name="countType"><see cref="MemberCountType"/> to count by</param>
2023-09-06 20:08:17 +02:00
/// <returns><see cref="int"/> with number of Members for passed in type</returns>
2017-12-28 09:18:09 +01:00
public int GetCount ( MemberCountType countType )
{
2022-06-07 15:28:38 +02:00
using ICoreScope scope = ScopeProvider . CreateCoreScope ( autoComplete : true ) ;
scope . ReadLock ( Constants . Locks . MemberTree ) ;
2017-12-28 09:18:09 +01:00
2022-06-07 15:28:38 +02:00
IQuery < IMember > ? query ;
2017-12-28 09:18:09 +01:00
2022-06-07 15:28:38 +02:00
switch ( countType )
{
case MemberCountType . All :
query = Query < IMember > ( ) ;
break ;
case MemberCountType . LockedOut :
query = Query < IMember > ( ) ? . Where ( x = > x . IsLockedOut = = true ) ;
break ;
case MemberCountType . Approved :
query = Query < IMember > ( ) ? . Where ( x = > x . IsApproved = = true ) ;
break ;
default :
throw new ArgumentOutOfRangeException ( nameof ( countType ) ) ;
2017-12-28 09:18:09 +01:00
}
2022-06-07 15:28:38 +02:00
return _memberRepository . GetCountByQuery ( query ) ;
2017-12-28 09:18:09 +01:00
}
/// <summary>
/// Gets the count of Members by an optional MemberType alias
/// </summary>
/// <remarks>If no alias is supplied then the count for all Member will be returned</remarks>
/// <param name="memberTypeAlias">Optional alias for the MemberType when counting number of Members</param>
2023-09-06 20:08:17 +02:00
/// <returns><see cref="int"/> with number of Members</returns>
2022-02-16 16:03:53 +01:00
public int Count ( string? memberTypeAlias = null )
2017-12-28 09:18:09 +01:00
{
2022-06-07 15:28:38 +02:00
using ICoreScope scope = ScopeProvider . CreateCoreScope ( autoComplete : true ) ;
scope . ReadLock ( Constants . Locks . MemberTree ) ;
return _memberRepository . Count ( memberTypeAlias ) ;
2017-12-28 09:18:09 +01:00
}
#endregion
#region Create
2024-03-14 14:24:10 +01:00
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 ) ) ;
}
2017-12-28 09:18:09 +01:00
/// <summary>
/// Creates an <see cref="IMember"/> object without persisting it
/// </summary>
/// <remarks>This method is convenient for when you need to add properties to a new Member
/// before persisting it in order to limit the amount of times its saved.
/// Also note that the returned <see cref="IMember"/> will not have an Id until its saved.</remarks>
/// <param name="username">Username of the Member to create</param>
/// <param name="email">Email of the Member to create</param>
/// <param name="name">Name of the Member to create</param>
/// <param name="memberTypeAlias">Alias of the MemberType the Member should be based on</param>
2020-12-05 23:44:50 +00:00
/// <exception cref="ArgumentException">Thrown when a member type for the given alias isn't found</exception>
2017-12-28 09:18:09 +01:00
/// <returns><see cref="IMember"/></returns>
public IMember CreateMember ( string username , string email , string name , string memberTypeAlias )
{
2020-12-05 23:44:50 +00:00
IMemberType memberType = GetMemberType ( memberTypeAlias ) ;
2017-12-28 09:18:09 +01:00
if ( memberType = = null )
2020-12-05 23:44:50 +00:00
{
2017-12-28 09:18:09 +01:00
throw new ArgumentException ( "No member type with that alias." , nameof ( memberTypeAlias ) ) ;
2020-12-05 23:44:50 +00:00
}
2017-12-28 09:18:09 +01:00
2021-02-01 16:07:42 +01:00
var member = new Member ( name , email . ToLower ( ) . Trim ( ) , username , memberType , 0 ) ;
2017-12-28 09:18:09 +01:00
return member ;
}
/// <summary>
/// Creates an <see cref="IMember"/> object without persisting it
/// </summary>
/// <remarks>This method is convenient for when you need to add properties to a new Member
/// before persisting it in order to limit the amount of times its saved.
/// Also note that the returned <see cref="IMember"/> will not have an Id until its saved.</remarks>
/// <param name="username">Username of the Member to create</param>
/// <param name="email">Email of the Member to create</param>
/// <param name="name">Name of the Member to create</param>
/// <param name="memberType">MemberType the Member should be based on</param>
/// <returns><see cref="IMember"/></returns>
public IMember CreateMember ( string username , string email , string name , IMemberType memberType )
{
2022-06-07 15:28:38 +02:00
if ( memberType = = null )
{
throw new ArgumentNullException ( nameof ( memberType ) ) ;
}
2017-12-28 09:18:09 +01:00
2021-02-01 16:07:42 +01:00
var member = new Member ( name , email . ToLower ( ) . Trim ( ) , username , memberType , 0 ) ;
2017-12-28 09:18:09 +01:00
return member ;
}
/// <summary>
/// Creates and persists a new <see cref="IMember"/>
/// </summary>
/// <remarks>An <see cref="IMembershipUser"/> can be of type <see cref="IMember"/> or <see cref="IUser"/></remarks>
/// <param name="username">Username of the <see cref="IMembershipUser"/> to create</param>
/// <param name="email">Email of the <see cref="IMembershipUser"/> to create</param>
/// <param name="passwordValue">This value should be the encoded/encrypted/hashed value for the password that will be stored in the database</param>
/// <param name="memberTypeAlias">Alias of the Type</param>
/// <returns><see cref="IMember"/></returns>
IMember IMembershipMemberService < IMember > . CreateWithIdentity ( string username , string email , string passwordValue , string memberTypeAlias )
2021-07-15 09:42:06 -06:00
= > CreateMemberWithIdentity ( username , email , username , passwordValue , memberTypeAlias ) ;
2017-12-28 09:18:09 +01:00
/// <summary>
/// Creates and persists a new <see cref="IMember"/>
/// </summary>
/// <remarks>An <see cref="IMembershipUser"/> can be of type <see cref="IMember"/> or <see cref="IUser"/></remarks>
/// <param name="username">Username of the <see cref="IMembershipUser"/> to create</param>
/// <param name="email">Email of the <see cref="IMembershipUser"/> to create</param>
/// <param name="passwordValue">This value should be the encoded/encrypted/hashed value for the password that will be stored in the database</param>
/// <param name="memberTypeAlias">Alias of the Type</param>
2018-05-03 14:58:34 +02:00
/// <param name="isApproved"></param>
2017-12-28 09:18:09 +01:00
/// <returns><see cref="IMember"/></returns>
2018-05-03 14:58:34 +02:00
IMember IMembershipMemberService < IMember > . CreateWithIdentity ( string username , string email , string passwordValue , string memberTypeAlias , bool isApproved )
2021-07-15 09:42:06 -06:00
= > CreateMemberWithIdentity ( username , email , username , passwordValue , memberTypeAlias , isApproved ) ;
2017-12-28 09:18:09 +01:00
public IMember CreateMemberWithIdentity ( string username , string email , string memberTypeAlias )
2022-06-07 15:28:38 +02:00
= > CreateMemberWithIdentity ( username , email , username , string . Empty , memberTypeAlias ) ;
2017-12-28 09:18:09 +01:00
public IMember CreateMemberWithIdentity ( string username , string email , string memberTypeAlias , bool isApproved )
2022-09-19 09:26:10 +01:00
= > CreateMemberWithIdentity ( username , email , username , string . Empty , memberTypeAlias , isApproved ) ;
2017-12-28 09:18:09 +01:00
public IMember CreateMemberWithIdentity ( string username , string email , string name , string memberTypeAlias )
2022-09-17 12:00:06 +01:00
= > CreateMemberWithIdentity ( username , email , name , string . Empty , memberTypeAlias ) ;
2017-12-28 09:18:09 +01:00
public IMember CreateMemberWithIdentity ( string username , string email , string name , string memberTypeAlias , bool isApproved )
2022-09-17 12:00:06 +01:00
= > CreateMemberWithIdentity ( username , email , name , string . Empty , memberTypeAlias , isApproved ) ;
2017-12-28 09:18:09 +01:00
/// <summary>
2023-09-06 20:08:17 +02:00
/// Creates and persists a Member.
2017-12-28 09:18:09 +01:00
/// </summary>
/// <remarks>Using this method will persist the Member object before its returned
2023-09-06 20:08:17 +02:00
/// meaning that it will have an Id available (unlike the <see cref="CreateMember(string, string, string, string)"/> method).</remarks>
/// <param name="username">Username of the Member to create.</param>
/// <param name="email">Email of the Member to create.</param>
/// <param name="name">Name of the Member to create.</param>
/// <param name="passwordValue">Password value of the Member to create.</param>
/// <param name="memberTypeAlias">Alias of the MemberType the Member should be based on.</param>
/// <param name="isApproved">Optional IsApproved of the Member to create.</param>
2017-12-28 09:18:09 +01:00
/// <returns><see cref="IMember"/></returns>
public IMember CreateMemberWithIdentity ( string username , string email , string name , string passwordValue , string memberTypeAlias , bool isApproved = true )
{
2022-04-26 10:22:37 +01:00
using ( ICoreScope scope = ScopeProvider . CreateCoreScope ( ) )
2017-12-28 09:18:09 +01:00
{
// locking the member tree secures member types too
scope . WriteLock ( Constants . Locks . MemberTree ) ;
2021-07-15 09:42:06 -06:00
IMemberType memberType = GetMemberType ( scope , memberTypeAlias ) ; // + locks // + locks
2017-12-28 09:18:09 +01:00
if ( memberType = = null )
2021-07-15 09:42:06 -06:00
{
2017-12-28 09:18:09 +01:00
throw new ArgumentException ( "No member type with that alias." , nameof ( memberTypeAlias ) ) ; // causes rollback // causes rollback
2021-07-15 09:42:06 -06:00
}
2017-12-28 09:18:09 +01:00
2021-02-01 16:07:42 +01:00
var member = new Member ( name , email . ToLower ( ) . Trim ( ) , username , passwordValue , memberType , isApproved , - 1 ) ;
2021-02-25 08:08:02 +01:00
Save ( member ) ;
2021-07-15 09:42:06 -06:00
scope . Complete ( ) ;
2017-12-28 09:18:09 +01:00
return member ;
}
}
public IMember CreateMemberWithIdentity ( string username , string email , IMemberType memberType )
2022-06-07 15:28:38 +02:00
= > CreateMemberWithIdentity ( username , email , username , string . Empty , memberType ) ;
2017-12-28 09:18:09 +01:00
/// <summary>
/// Creates and persists a Member
/// </summary>
/// <remarks>Using this method will persist the Member object before its returned
/// meaning that it will have an Id available (unlike the CreateMember method)</remarks>
/// <param name="username">Username of the Member to create</param>
/// <param name="email">Email of the Member to create</param>
/// <param name="memberType">MemberType the Member should be based on</param>
2023-09-06 20:08:17 +02:00
/// <param name="isApproved">Is the member approved.</param>
2017-12-28 09:18:09 +01:00
/// <returns><see cref="IMember"/></returns>
public IMember CreateMemberWithIdentity ( string username , string email , IMemberType memberType , bool isApproved )
2022-06-07 15:28:38 +02:00
= > CreateMemberWithIdentity ( username , email , username , string . Empty , memberType , isApproved ) ;
2017-12-28 09:18:09 +01:00
public IMember CreateMemberWithIdentity ( string username , string email , string name , IMemberType memberType )
2022-06-07 15:28:38 +02:00
= > CreateMemberWithIdentity ( username , email , name , string . Empty , memberType ) ;
2017-12-28 09:18:09 +01:00
/// <summary>
/// Creates and persists a Member
/// </summary>
/// <remarks>Using this method will persist the Member object before its returned
/// meaning that it will have an Id available (unlike the CreateMember method)</remarks>
/// <param name="username">Username of the Member to create</param>
/// <param name="email">Email of the Member to create</param>
/// <param name="name">Name of the Member to create</param>
/// <param name="memberType">MemberType the Member should be based on</param>
2023-09-06 20:08:17 +02:00
/// <param name="isApproved">Is the member approved</param>
2017-12-28 09:18:09 +01:00
/// <returns><see cref="IMember"/></returns>
public IMember CreateMemberWithIdentity ( string username , string email , string name , IMemberType memberType , bool isApproved )
2022-06-07 15:28:38 +02:00
= > CreateMemberWithIdentity ( username , email , name , string . Empty , memberType , isApproved ) ;
2017-12-28 09:18:09 +01:00
/// <summary>
/// Creates and persists a Member
/// </summary>
/// <remarks>Using this method will persist the Member object before its returned
/// meaning that it will have an Id available (unlike the CreateMember method)</remarks>
/// <param name="username">Username of the Member to create</param>
/// <param name="email">Email of the Member to create</param>
/// <param name="name">Name of the Member to create</param>
/// <param name="passwordValue">This value should be the encoded/encrypted/hashed value for the password that will be stored in the database</param>
/// <param name="memberType">MemberType the Member should be based on</param>
2023-09-06 20:08:17 +02:00
/// <param name="isApproved">Is the member approved</param>
2017-12-28 09:18:09 +01:00
/// <returns><see cref="IMember"/></returns>
private IMember CreateMemberWithIdentity ( string username , string email , string name , string passwordValue , IMemberType memberType , bool isApproved = true )
{
2022-06-07 15:28:38 +02:00
if ( memberType = = null )
2017-12-28 09:18:09 +01:00
{
2022-06-07 15:28:38 +02:00
throw new ArgumentNullException ( nameof ( memberType ) ) ;
}
2017-12-28 09:18:09 +01:00
2022-06-07 15:28:38 +02:00
using ICoreScope scope = ScopeProvider . CreateCoreScope ( ) ;
scope . WriteLock ( Constants . Locks . MemberTree ) ;
2017-12-28 09:18:09 +01:00
2022-06-07 15:28:38 +02:00
// ensure it all still make sense
// ensure it all still make sense
IMemberType ? vrfy = GetMemberType ( scope , memberType . Alias ) ; // + locks
2021-07-15 09:42:06 -06:00
2022-06-07 15:28:38 +02:00
if ( vrfy = = null | | vrfy . Id ! = memberType . Id )
{
throw new ArgumentException ( $"Member type with alias {memberType.Alias} does not exist or is a different member type." ) ; // causes rollback
}
2017-12-28 09:18:09 +01:00
2022-06-07 15:28:38 +02:00
var member = new Member ( name , email . ToLower ( ) . Trim ( ) , username , passwordValue , memberType , isApproved , - 1 ) ;
2021-07-15 09:42:06 -06:00
2022-06-07 15:28:38 +02:00
Save ( member ) ;
2021-07-15 09:42:06 -06:00
2022-06-07 15:28:38 +02:00
scope . Complete ( ) ;
return member ;
2017-12-28 09:18:09 +01:00
}
#endregion
#region Get , Has , Is , Exists . . .
/// <summary>
/// Gets a Member by its integer id
/// </summary>
2023-09-06 20:08:17 +02:00
/// <param name="id"><see cref="int"/> Id</param>
2017-12-28 09:18:09 +01:00
/// <returns><see cref="IMember"/></returns>
2022-02-16 16:03:53 +01:00
public IMember ? GetById ( int id )
2017-12-28 09:18:09 +01:00
{
2022-06-07 15:28:38 +02:00
using ICoreScope scope = ScopeProvider . CreateCoreScope ( autoComplete : true ) ;
scope . ReadLock ( Constants . Locks . MemberTree ) ;
return _memberRepository . Get ( id ) ;
2017-12-28 09:18:09 +01:00
}
/// <summary>
/// Gets a Member by the unique key
/// </summary>
/// <remarks>The guid key corresponds to the unique id in the database
/// and the user id in the membership provider.</remarks>
/// <param name="id"><see cref="Guid"/> Id</param>
/// <returns><see cref="IMember"/></returns>
2024-02-05 06:42:07 +01:00
public IMember ? GetById ( Guid id )
2017-12-28 09:18:09 +01:00
{
2022-06-07 15:28:38 +02:00
using ICoreScope scope = ScopeProvider . CreateCoreScope ( autoComplete : true ) ;
scope . ReadLock ( Constants . Locks . MemberTree ) ;
2024-04-11 13:53:34 +02:00
return GetMemberFromRepository ( id ) ;
2017-12-28 09:18:09 +01:00
}
2024-02-05 06:42:07 +01:00
[Obsolete($"Use {nameof(GetById)}. Will be removed in V15.")]
public IMember ? GetByKey ( Guid id )
= > GetById ( id ) ;
2017-12-28 09:18:09 +01:00
/// <summary>
/// Gets a list of paged <see cref="IMember"/> objects
/// </summary>
/// <param name="pageIndex">Current page index</param>
/// <param name="pageSize">Size of the page</param>
/// <param name="totalRecords">Total number of records found (out)</param>
/// <returns><see cref="IEnumerable{IMember}"/></returns>
public IEnumerable < IMember > GetAll ( long pageIndex , int pageSize , out long totalRecords )
{
2022-06-07 15:28:38 +02:00
using ICoreScope scope = ScopeProvider . CreateCoreScope ( autoComplete : true ) ;
scope . ReadLock ( Constants . Locks . MemberTree ) ;
return _memberRepository . GetPage ( null , pageIndex , pageSize , out totalRecords , null , Ordering . By ( "LoginName" ) ) ;
2017-12-28 09:18:09 +01:00
}
2022-06-07 15:28:38 +02:00
public IEnumerable < IMember > GetAll (
long pageIndex ,
int pageSize ,
out long totalRecords ,
string orderBy ,
Direction orderDirection ,
string? memberTypeAlias = null ,
string filter = "" ) = >
GetAll ( pageIndex , pageSize , out totalRecords , orderBy , orderDirection , true , memberTypeAlias , filter ) ;
public IEnumerable < IMember > GetAll (
long pageIndex ,
int pageSize ,
out long totalRecords ,
string orderBy ,
Direction orderDirection ,
bool orderBySystemField ,
string? memberTypeAlias ,
string filter )
2017-12-28 09:18:09 +01:00
{
2022-06-07 15:28:38 +02:00
using ICoreScope scope = ScopeProvider . CreateCoreScope ( autoComplete : true ) ;
scope . ReadLock ( Constants . Locks . MemberTree ) ;
IQuery < IMember > ? query1 = memberTypeAlias = = null ? null : Query < IMember > ( ) ? . Where ( x = > x . ContentTypeAlias = = memberTypeAlias ) ;
2023-09-07 15:07:35 +02:00
int . TryParse ( filter , out int filterAsIntId ) ; //considering id,key & name as filter param
Guid . TryParse ( filter , out Guid filterAsGuid ) ;
IQuery < IMember > ? query2 = filter = = null ? null : Query < IMember > ( ) ? . Where ( x = > ( x . Name ! = null & & x . Name . Contains ( filter ) ) | | x . Username . Contains ( filter ) | | x . Email . Contains ( filter ) | | x . Id = = filterAsIntId | | x . Key = = filterAsGuid ) ;
2022-06-07 15:28:38 +02:00
return _memberRepository . GetPage ( query1 , pageIndex , pageSize , out totalRecords , query2 , Ordering . By ( orderBy , orderDirection , isCustomField : ! orderBySystemField ) ) ;
2017-12-28 09:18:09 +01:00
}
/// <summary>
/// Gets an <see cref="IMember"/> by its provider key
/// </summary>
/// <param name="id">Id to use for retrieval</param>
/// <returns><see cref="IMember"/></returns>
2022-02-15 15:48:41 +01:00
public IMember ? GetByProviderKey ( object id )
2017-12-28 09:18:09 +01:00
{
2022-06-07 15:28:38 +02:00
Attempt < Guid > asGuid = id . TryConvertTo < Guid > ( ) ;
2017-12-28 09:18:09 +01:00
if ( asGuid . Success )
2022-06-07 15:28:38 +02:00
{
2017-12-28 09:18:09 +01:00
return GetByKey ( asGuid . Result ) ;
2022-06-07 15:28:38 +02:00
}
2017-12-28 09:18:09 +01:00
2022-06-07 15:28:38 +02:00
Attempt < int > asInt = id . TryConvertTo < int > ( ) ;
2017-12-28 09:18:09 +01:00
if ( asInt . Success )
2022-06-07 15:28:38 +02:00
{
2017-12-28 09:18:09 +01:00
return GetById ( asInt . Result ) ;
2022-06-07 15:28:38 +02:00
}
2017-12-28 09:18:09 +01:00
return null ;
}
/// <summary>
/// Get an <see cref="IMember"/> by email
/// </summary>
/// <param name="email">Email to use for retrieval</param>
/// <returns><see cref="IMember"/></returns>
2022-02-16 16:03:53 +01:00
public IMember ? GetByEmail ( string email )
2017-12-28 09:18:09 +01:00
{
2022-06-07 15:28:38 +02:00
using ICoreScope scope = ScopeProvider . CreateCoreScope ( autoComplete : true ) ;
scope . ReadLock ( Constants . Locks . MemberTree ) ;
IQuery < IMember > query = Query < IMember > ( ) . Where ( x = > x . Email . Equals ( email ) ) ;
return _memberRepository . Get ( query ) ? . FirstOrDefault ( ) ;
2017-12-28 09:18:09 +01:00
}
/// <summary>
/// Get an <see cref="IMember"/> by username
/// </summary>
/// <param name="username">Username to use for retrieval</param>
/// <returns><see cref="IMember"/></returns>
2022-03-31 15:57:23 +02:00
public IMember ? GetByUsername ( string? username )
2017-12-28 09:18:09 +01:00
{
2022-06-07 15:28:38 +02:00
using ICoreScope scope = ScopeProvider . CreateCoreScope ( autoComplete : true ) ;
scope . ReadLock ( Constants . Locks . MemberTree ) ;
return _memberRepository . GetByUsername ( username ) ;
2017-12-28 09:18:09 +01:00
}
/// <summary>
/// Gets all Members for the specified MemberType alias
/// </summary>
/// <param name="memberTypeAlias">Alias of the MemberType</param>
/// <returns><see cref="IEnumerable{IMember}"/></returns>
2022-05-06 10:13:58 +02:00
public IEnumerable < IMember > GetMembersByMemberType ( string memberTypeAlias )
2017-12-28 09:18:09 +01:00
{
2022-06-07 15:28:38 +02:00
using ICoreScope scope = ScopeProvider . CreateCoreScope ( autoComplete : true ) ;
scope . ReadLock ( Constants . Locks . MemberTree ) ;
IQuery < IMember > query = Query < IMember > ( ) . Where ( x = > x . ContentTypeAlias = = memberTypeAlias ) ;
return _memberRepository . Get ( query ) ;
2017-12-28 09:18:09 +01:00
}
/// <summary>
/// Gets all Members for the MemberType id
/// </summary>
/// <param name="memberTypeId">Id of the MemberType</param>
/// <returns><see cref="IEnumerable{IMember}"/></returns>
2022-05-06 10:13:58 +02:00
public IEnumerable < IMember > GetMembersByMemberType ( int memberTypeId )
2017-12-28 09:18:09 +01:00
{
2022-06-07 15:28:38 +02:00
using ICoreScope scope = ScopeProvider . CreateCoreScope ( autoComplete : true ) ;
scope . ReadLock ( Constants . Locks . MemberTree ) ;
IQuery < IMember > query = Query < IMember > ( ) . Where ( x = > x . ContentTypeId = = memberTypeId ) ;
return _memberRepository . Get ( query ) ;
2017-12-28 09:18:09 +01:00
}
/// <summary>
/// Gets all Members within the specified MemberGroup name
/// </summary>
/// <param name="memberGroupName">Name of the MemberGroup</param>
/// <returns><see cref="IEnumerable{IMember}"/></returns>
public IEnumerable < IMember > GetMembersByGroup ( string memberGroupName )
{
2022-06-07 15:28:38 +02:00
using ICoreScope scope = ScopeProvider . CreateCoreScope ( autoComplete : true ) ;
scope . ReadLock ( Constants . Locks . MemberTree ) ;
return _memberRepository . GetByMemberGroup ( memberGroupName ) ;
2017-12-28 09:18:09 +01:00
}
/// <summary>
/// Gets all Members with the ids specified
/// </summary>
/// <remarks>If no Ids are specified all Members will be retrieved</remarks>
/// <param name="ids">Optional list of Member Ids</param>
/// <returns><see cref="IEnumerable{IMember}"/></returns>
public IEnumerable < IMember > GetAllMembers ( params int [ ] ids )
{
2022-06-07 15:28:38 +02:00
using ICoreScope scope = ScopeProvider . CreateCoreScope ( autoComplete : true ) ;
scope . ReadLock ( Constants . Locks . MemberTree ) ;
return _memberRepository . GetMany ( ids ) ;
2017-12-28 09:18:09 +01:00
}
2023-04-19 09:43:01 +02:00
/// <inheritdoc />
public Task < IEnumerable < IMember > > GetByKeysAsync ( params Guid [ ] ids )
{
using ICoreScope scope = ScopeProvider . CreateCoreScope ( autoComplete : true ) ;
scope . ReadLock ( Constants . Locks . MemberTree ) ;
IQuery < IMember > query = Query < IMember > ( ) . Where ( x = > ids . Contains ( x . Key ) ) ;
return Task . FromResult ( _memberRepository . Get ( query ) ) ;
}
2017-12-28 09:18:09 +01:00
/// <summary>
/// Finds Members based on their display name
/// </summary>
/// <param name="displayNameToMatch">Display name to match</param>
/// <param name="pageIndex">Current page index</param>
/// <param name="pageSize">Size of the page</param>
/// <param name="totalRecords">Total number of records found (out)</param>
/// <param name="matchType">The type of match to make as <see cref="StringPropertyMatchType"/>. Default is <see cref="StringPropertyMatchType.StartsWith"/></param>
/// <returns><see cref="IEnumerable{IMember}"/></returns>
public IEnumerable < IMember > FindMembersByDisplayName ( string displayNameToMatch , long pageIndex , int pageSize , out long totalRecords , StringPropertyMatchType matchType = StringPropertyMatchType . StartsWith )
{
2022-06-07 15:28:38 +02:00
using ICoreScope scope = ScopeProvider . CreateCoreScope ( autoComplete : true ) ;
scope . ReadLock ( Constants . Locks . MemberTree ) ;
IQuery < IMember > ? query = Query < IMember > ( ) ;
2017-12-28 09:18:09 +01:00
2022-06-07 15:28:38 +02:00
switch ( matchType )
{
case StringPropertyMatchType . Exact :
query ? . Where ( member = > string . Equals ( member . Name , displayNameToMatch ) ) ;
break ;
case StringPropertyMatchType . Contains :
query ? . Where ( member = > member . Name ! = null & & member . Name . Contains ( displayNameToMatch ) ) ;
break ;
case StringPropertyMatchType . StartsWith :
query ? . Where ( member = > member . Name ! = null & & member . Name . StartsWith ( displayNameToMatch ) ) ;
break ;
case StringPropertyMatchType . EndsWith :
query ? . Where ( member = > member . Name ! = null & & member . Name . EndsWith ( displayNameToMatch ) ) ;
break ;
case StringPropertyMatchType . Wildcard :
query ? . Where ( member = > member . Name ! = null & & member . Name . SqlWildcard ( displayNameToMatch , TextColumnType . NVarchar ) ) ;
break ;
default :
throw new ArgumentOutOfRangeException ( nameof ( matchType ) ) ; // causes rollback // causes rollback
2017-12-28 09:18:09 +01:00
}
2022-06-07 15:28:38 +02:00
return _memberRepository . GetPage ( query , pageIndex , pageSize , out totalRecords , null , Ordering . By ( "Name" ) ) ;
2017-12-28 09:18:09 +01:00
}
2020-01-07 13:50:38 +01:00
2017-12-28 09:18:09 +01:00
/// <summary>
/// Finds a list of <see cref="IMember"/> objects by a partial email string
/// </summary>
/// <param name="emailStringToMatch">Partial email string to match</param>
/// <param name="pageIndex">Current page index</param>
/// <param name="pageSize">Size of the page</param>
/// <param name="totalRecords">Total number of records found (out)</param>
/// <param name="matchType">The type of match to make as <see cref="StringPropertyMatchType"/>. Default is <see cref="StringPropertyMatchType.StartsWith"/></param>
/// <returns><see cref="IEnumerable{IMember}"/></returns>
public IEnumerable < IMember > FindByEmail ( string emailStringToMatch , long pageIndex , int pageSize , out long totalRecords , StringPropertyMatchType matchType = StringPropertyMatchType . StartsWith )
{
2022-06-07 15:28:38 +02:00
using ICoreScope scope = ScopeProvider . CreateCoreScope ( autoComplete : true ) ;
scope . ReadLock ( Constants . Locks . MemberTree ) ;
IQuery < IMember > ? query = Query < IMember > ( ) ;
2017-12-28 09:18:09 +01:00
2022-06-07 15:28:38 +02:00
switch ( matchType )
{
case StringPropertyMatchType . Exact :
query ? . Where ( member = > member . Email . Equals ( emailStringToMatch ) ) ;
break ;
case StringPropertyMatchType . Contains :
query ? . Where ( member = > member . Email . Contains ( emailStringToMatch ) ) ;
break ;
case StringPropertyMatchType . StartsWith :
query ? . Where ( member = > member . Email . StartsWith ( emailStringToMatch ) ) ;
break ;
case StringPropertyMatchType . EndsWith :
query ? . Where ( member = > member . Email . EndsWith ( emailStringToMatch ) ) ;
break ;
case StringPropertyMatchType . Wildcard :
query ? . Where ( member = > member . Email . SqlWildcard ( emailStringToMatch , TextColumnType . NVarchar ) ) ;
break ;
default :
throw new ArgumentOutOfRangeException ( nameof ( matchType ) ) ;
2017-12-28 09:18:09 +01:00
}
2022-06-07 15:28:38 +02:00
return _memberRepository . GetPage ( query , pageIndex , pageSize , out totalRecords , null , Ordering . By ( "Email" ) ) ;
2017-12-28 09:18:09 +01:00
}
/// <summary>
/// Finds a list of <see cref="IMember"/> objects by a partial username
/// </summary>
/// <param name="login">Partial username to match</param>
/// <param name="pageIndex">Current page index</param>
/// <param name="pageSize">Size of the page</param>
/// <param name="totalRecords">Total number of records found (out)</param>
/// <param name="matchType">The type of match to make as <see cref="StringPropertyMatchType"/>. Default is <see cref="StringPropertyMatchType.StartsWith"/></param>
/// <returns><see cref="IEnumerable{IMember}"/></returns>
public IEnumerable < IMember > FindByUsername ( string login , long pageIndex , int pageSize , out long totalRecords , StringPropertyMatchType matchType = StringPropertyMatchType . StartsWith )
{
2022-06-07 15:28:38 +02:00
using ICoreScope scope = ScopeProvider . CreateCoreScope ( autoComplete : true ) ;
scope . ReadLock ( Constants . Locks . MemberTree ) ;
IQuery < IMember > ? query = Query < IMember > ( ) ;
2017-12-28 09:18:09 +01:00
2022-06-07 15:28:38 +02:00
switch ( matchType )
{
case StringPropertyMatchType . Exact :
query ? . Where ( member = > member . Username . Equals ( login ) ) ;
break ;
case StringPropertyMatchType . Contains :
query ? . Where ( member = > member . Username . Contains ( login ) ) ;
break ;
case StringPropertyMatchType . StartsWith :
query ? . Where ( member = > member . Username . StartsWith ( login ) ) ;
break ;
case StringPropertyMatchType . EndsWith :
query ? . Where ( member = > member . Username . EndsWith ( login ) ) ;
break ;
case StringPropertyMatchType . Wildcard :
query ? . Where ( member = > member . Username . SqlWildcard ( login , TextColumnType . NVarchar ) ) ;
break ;
default :
throw new ArgumentOutOfRangeException ( nameof ( matchType ) ) ;
2017-12-28 09:18:09 +01:00
}
2022-06-07 15:28:38 +02:00
return _memberRepository . GetPage ( query , pageIndex , pageSize , out totalRecords , null , Ordering . By ( "LoginName" ) ) ;
2017-12-28 09:18:09 +01:00
}
/// <summary>
/// Gets a list of Members based on a property search
/// </summary>
/// <param name="propertyTypeAlias">Alias of the PropertyType to search for</param>
2023-09-06 20:08:17 +02:00
/// <param name="value"><see cref="string"/> Value to match</param>
2017-12-28 09:18:09 +01:00
/// <param name="matchType">The type of match to make as <see cref="StringPropertyMatchType"/>. Default is <see cref="StringPropertyMatchType.Exact"/></param>
/// <returns><see cref="IEnumerable{IMember}"/></returns>
2022-02-28 13:14:02 +01:00
public IEnumerable < IMember > ? GetMembersByPropertyValue ( string propertyTypeAlias , string value , StringPropertyMatchType matchType = StringPropertyMatchType . Exact )
2017-12-28 09:18:09 +01:00
{
2022-06-07 15:28:38 +02:00
using ICoreScope scope = ScopeProvider . CreateCoreScope ( autoComplete : true ) ;
scope . ReadLock ( Constants . Locks . MemberTree ) ;
IQuery < IMember > query ;
2017-12-28 09:18:09 +01:00
2022-06-07 15:28:38 +02:00
switch ( matchType )
{
case StringPropertyMatchType . Exact :
query = Query < IMember > ( ) . Where ( x = > ( ( Member ) x ) . PropertyTypeAlias = = propertyTypeAlias & & ( ( ( Member ) x ) . LongStringPropertyValue ! . SqlEquals ( value , TextColumnType . NText ) | | ( ( Member ) x ) . ShortStringPropertyValue ! . SqlEquals ( value , TextColumnType . NVarchar ) ) ) ;
break ;
case StringPropertyMatchType . Contains :
query = Query < IMember > ( ) . Where ( x = > ( ( Member ) x ) . PropertyTypeAlias = = propertyTypeAlias & & ( ( ( Member ) x ) . LongStringPropertyValue ! . SqlContains ( value , TextColumnType . NText ) | | ( ( Member ) x ) . ShortStringPropertyValue ! . SqlContains ( value , TextColumnType . NVarchar ) ) ) ;
break ;
case StringPropertyMatchType . StartsWith :
query = Query < IMember > ( ) . Where ( x = > ( ( Member ) x ) . PropertyTypeAlias = = propertyTypeAlias & & ( ( ( Member ) x ) . LongStringPropertyValue . SqlStartsWith ( value , TextColumnType . NText ) | | ( ( Member ) x ) . ShortStringPropertyValue . SqlStartsWith ( value , TextColumnType . NVarchar ) ) ) ;
break ;
case StringPropertyMatchType . EndsWith :
query = Query < IMember > ( ) . Where ( x = > ( ( Member ) x ) . PropertyTypeAlias = = propertyTypeAlias & & ( ( ( Member ) x ) . LongStringPropertyValue ! . SqlEndsWith ( value , TextColumnType . NText ) | | ( ( Member ) x ) . ShortStringPropertyValue ! . SqlEndsWith ( value , TextColumnType . NVarchar ) ) ) ;
break ;
default :
throw new ArgumentOutOfRangeException ( nameof ( matchType ) ) ;
2017-12-28 09:18:09 +01:00
}
2022-06-07 15:28:38 +02:00
return _memberRepository . Get ( query ) ;
2017-12-28 09:18:09 +01:00
}
/// <summary>
/// Gets a list of Members based on a property search
/// </summary>
/// <param name="propertyTypeAlias">Alias of the PropertyType to search for</param>
2023-09-06 20:08:17 +02:00
/// <param name="value"><see cref="int"/> Value to match</param>
2017-12-28 09:18:09 +01:00
/// <param name="matchType">The type of match to make as <see cref="StringPropertyMatchType"/>. Default is <see cref="StringPropertyMatchType.Exact"/></param>
/// <returns><see cref="IEnumerable{IMember}"/></returns>
2022-02-28 13:14:02 +01:00
public IEnumerable < IMember > ? GetMembersByPropertyValue ( string propertyTypeAlias , int value , ValuePropertyMatchType matchType = ValuePropertyMatchType . Exact )
2017-12-28 09:18:09 +01:00
{
2022-06-07 15:28:38 +02:00
using ICoreScope scope = ScopeProvider . CreateCoreScope ( autoComplete : true ) ;
scope . ReadLock ( Constants . Locks . MemberTree ) ;
IQuery < IMember > query ;
2017-12-28 09:18:09 +01:00
2022-06-07 15:28:38 +02:00
switch ( matchType )
{
case ValuePropertyMatchType . Exact :
query = Query < IMember > ( ) . Where ( x = > ( ( Member ) x ) . PropertyTypeAlias = = propertyTypeAlias & & ( ( Member ) x ) . IntegerPropertyValue = = value ) ;
break ;
case ValuePropertyMatchType . GreaterThan :
query = Query < IMember > ( ) . Where ( x = > ( ( Member ) x ) . PropertyTypeAlias = = propertyTypeAlias & & ( ( Member ) x ) . IntegerPropertyValue > value ) ;
break ;
case ValuePropertyMatchType . LessThan :
query = Query < IMember > ( ) . Where ( x = > ( ( Member ) x ) . PropertyTypeAlias = = propertyTypeAlias & & ( ( Member ) x ) . IntegerPropertyValue < value ) ;
break ;
case ValuePropertyMatchType . GreaterThanOrEqualTo :
query = Query < IMember > ( ) . Where ( x = > ( ( Member ) x ) . PropertyTypeAlias = = propertyTypeAlias & & ( ( Member ) x ) . IntegerPropertyValue > = value ) ;
break ;
case ValuePropertyMatchType . LessThanOrEqualTo :
query = Query < IMember > ( ) . Where ( x = > ( ( Member ) x ) . PropertyTypeAlias = = propertyTypeAlias & & ( ( Member ) x ) . IntegerPropertyValue < = value ) ;
break ;
default :
throw new ArgumentOutOfRangeException ( nameof ( matchType ) ) ;
2017-12-28 09:18:09 +01:00
}
2022-06-07 15:28:38 +02:00
return _memberRepository . Get ( query ) ;
2017-12-28 09:18:09 +01:00
}
/// <summary>
/// Gets a list of Members based on a property search
/// </summary>
/// <param name="propertyTypeAlias">Alias of the PropertyType to search for</param>
2023-09-06 20:08:17 +02:00
/// <param name="value"><see cref="bool"/> Value to match</param>
2017-12-28 09:18:09 +01:00
/// <returns><see cref="IEnumerable{IMember}"/></returns>
2022-02-28 13:14:02 +01:00
public IEnumerable < IMember > ? GetMembersByPropertyValue ( string propertyTypeAlias , bool value )
2017-12-28 09:18:09 +01:00
{
2022-06-07 15:28:38 +02:00
using ICoreScope scope = ScopeProvider . CreateCoreScope ( autoComplete : true ) ;
scope . ReadLock ( Constants . Locks . MemberTree ) ;
IQuery < IMember > query = Query < IMember > ( ) . Where ( x = > ( ( Member ) x ) . PropertyTypeAlias = = propertyTypeAlias & & ( ( Member ) x ) . BoolPropertyValue = = value ) ;
2017-12-28 09:18:09 +01:00
2022-06-07 15:28:38 +02:00
return _memberRepository . Get ( query ) ;
2017-12-28 09:18:09 +01:00
}
/// <summary>
/// Gets a list of Members based on a property search
/// </summary>
/// <param name="propertyTypeAlias">Alias of the PropertyType to search for</param>
/// <param name="value"><see cref="System.DateTime"/> Value to match</param>
/// <param name="matchType">The type of match to make as <see cref="StringPropertyMatchType"/>. Default is <see cref="StringPropertyMatchType.Exact"/></param>
/// <returns><see cref="IEnumerable{IMember}"/></returns>
2022-02-28 13:14:02 +01:00
public IEnumerable < IMember > ? GetMembersByPropertyValue ( string propertyTypeAlias , DateTime value , ValuePropertyMatchType matchType = ValuePropertyMatchType . Exact )
2017-12-28 09:18:09 +01:00
{
2022-06-07 15:28:38 +02:00
using ICoreScope scope = ScopeProvider . CreateCoreScope ( autoComplete : true ) ;
scope . ReadLock ( Constants . Locks . MemberTree ) ;
IQuery < IMember > query ;
2017-12-28 09:18:09 +01:00
2022-06-07 15:28:38 +02:00
switch ( matchType )
{
case ValuePropertyMatchType . Exact :
query = Query < IMember > ( ) . Where ( x = > ( ( Member ) x ) . PropertyTypeAlias = = propertyTypeAlias & & ( ( Member ) x ) . DateTimePropertyValue = = value ) ;
break ;
case ValuePropertyMatchType . GreaterThan :
query = Query < IMember > ( ) . Where ( x = > ( ( Member ) x ) . PropertyTypeAlias = = propertyTypeAlias & & ( ( Member ) x ) . DateTimePropertyValue > value ) ;
break ;
case ValuePropertyMatchType . LessThan :
query = Query < IMember > ( ) . Where ( x = > ( ( Member ) x ) . PropertyTypeAlias = = propertyTypeAlias & & ( ( Member ) x ) . DateTimePropertyValue < value ) ;
break ;
case ValuePropertyMatchType . GreaterThanOrEqualTo :
query = Query < IMember > ( ) . Where ( x = > ( ( Member ) x ) . PropertyTypeAlias = = propertyTypeAlias & & ( ( Member ) x ) . DateTimePropertyValue > = value ) ;
break ;
case ValuePropertyMatchType . LessThanOrEqualTo :
query = Query < IMember > ( ) . Where ( x = > ( ( Member ) x ) . PropertyTypeAlias = = propertyTypeAlias & & ( ( Member ) x ) . DateTimePropertyValue < = value ) ;
break ;
default :
throw new ArgumentOutOfRangeException ( nameof ( matchType ) ) ; // causes rollback // causes rollback
2017-12-28 09:18:09 +01:00
}
2022-06-07 15:28:38 +02:00
// TODO: Since this is by property value, we need a GetByPropertyQuery on the repo!
return _memberRepository . Get ( query ) ;
2017-12-28 09:18:09 +01:00
}
/// <summary>
/// Checks if a Member with the id exists
/// </summary>
/// <param name="id">Id of the Member</param>
/// <returns><c>True</c> if the Member exists otherwise <c>False</c></returns>
public bool Exists ( int id )
{
2022-06-07 15:28:38 +02:00
using ICoreScope scope = ScopeProvider . CreateCoreScope ( autoComplete : true ) ;
scope . ReadLock ( Constants . Locks . MemberTree ) ;
return _memberRepository . Exists ( id ) ;
2017-12-28 09:18:09 +01:00
}
/// <summary>
/// Checks if a Member with the username exists
/// </summary>
/// <param name="username">Username to check</param>
/// <returns><c>True</c> if the Member exists otherwise <c>False</c></returns>
public bool Exists ( string username )
{
2022-06-07 15:28:38 +02:00
using ICoreScope scope = ScopeProvider . CreateCoreScope ( autoComplete : true ) ;
scope . ReadLock ( Constants . Locks . MemberTree ) ;
return _memberRepository . Exists ( username ) ;
2017-12-28 09:18:09 +01:00
}
#endregion
#region Save
2022-09-19 16:37:24 +02:00
public void SetLastLogin ( string username , DateTime date ) = > throw new NotImplementedException ( ) ;
2020-07-30 22:56:31 +10:00
/// <inheritdoc />
2024-02-05 06:42:07 +01:00
public Attempt < OperationResult ? > Save ( IMember member , int userId = Constants . Security . SuperUserId )
2017-12-28 09:18:09 +01:00
{
2021-01-29 11:41:58 +00:00
// trimming username and email to make sure we have no trailing space
2018-03-22 17:41:13 +01:00
member . Username = member . Username . Trim ( ) ;
2019-12-18 10:32:22 +01:00
member . Email = member . Email . Trim ( ) ;
2022-06-07 15:28:38 +02:00
EventMessages evtMsgs = EventMessagesFactory . Get ( ) ;
2021-03-09 07:52:32 +01:00
2022-06-07 15:28:38 +02:00
using ICoreScope scope = ScopeProvider . CreateCoreScope ( ) ;
var savingNotification = new MemberSavingNotification ( member , evtMsgs ) ;
if ( scope . Notifications . PublishCancelable ( savingNotification ) )
2017-12-28 09:18:09 +01:00
{
2022-06-07 15:28:38 +02:00
scope . Complete ( ) ;
2024-02-05 06:42:07 +01:00
return OperationResult . Attempt . Cancel ( evtMsgs ) ;
2022-06-07 15:28:38 +02:00
}
2017-12-28 09:18:09 +01:00
2022-06-07 15:28:38 +02:00
if ( string . IsNullOrWhiteSpace ( member . Name ) )
{
throw new ArgumentException ( "Cannot save member with empty name." ) ;
}
2017-12-28 09:18:09 +01:00
2022-06-07 15:28:38 +02:00
scope . WriteLock ( Constants . Locks . MemberTree ) ;
2017-12-28 09:18:09 +01:00
2022-06-07 15:28:38 +02:00
_memberRepository . Save ( member ) ;
2017-12-28 09:18:09 +01:00
2022-06-07 15:28:38 +02:00
scope . Notifications . Publish ( new MemberSavedNotification ( member , evtMsgs ) . WithStateFrom ( savingNotification ) ) ;
2021-01-29 11:41:58 +00:00
2022-06-07 15:28:38 +02:00
Audit ( AuditType . Save , 0 , member . Id ) ;
2017-12-28 09:18:09 +01:00
2022-06-07 15:28:38 +02:00
scope . Complete ( ) ;
2024-02-05 06:42:07 +01:00
return OperationResult . Attempt . Succeed ( evtMsgs ) ;
2017-12-28 09:18:09 +01:00
}
2024-02-05 06:42:07 +01:00
public void Save ( IMember member )
= > Save ( member , Constants . Security . SuperUserId ) ;
2020-07-30 22:56:31 +10:00
/// <inheritdoc />
2024-02-05 06:42:07 +01:00
public Attempt < OperationResult ? > Save ( IEnumerable < IMember > members , int userId = Constants . Security . SuperUserId )
2017-12-28 09:18:09 +01:00
{
2022-06-07 15:28:38 +02:00
IMember [ ] membersA = members . ToArray ( ) ;
2017-12-28 09:18:09 +01:00
2022-06-07 15:28:38 +02:00
EventMessages evtMsgs = EventMessagesFactory . Get ( ) ;
2021-03-09 07:52:32 +01:00
2022-06-07 15:28:38 +02:00
using ICoreScope scope = ScopeProvider . CreateCoreScope ( ) ;
var savingNotification = new MemberSavingNotification ( membersA , evtMsgs ) ;
if ( scope . Notifications . PublishCancelable ( savingNotification ) )
2017-12-28 09:18:09 +01:00
{
2022-06-07 15:28:38 +02:00
scope . Complete ( ) ;
2024-02-05 06:42:07 +01:00
return OperationResult . Attempt . Cancel ( evtMsgs ) ;
2022-06-07 15:28:38 +02:00
}
2017-12-28 09:18:09 +01:00
2022-06-07 15:28:38 +02:00
scope . WriteLock ( Constants . Locks . MemberTree ) ;
2017-12-28 09:18:09 +01:00
2022-06-07 15:28:38 +02:00
foreach ( IMember member in membersA )
{
//trimming username and email to make sure we have no trailing space
member . Username = member . Username . Trim ( ) ;
member . Email = member . Email . Trim ( ) ;
2018-03-22 17:41:13 +01:00
2022-06-07 15:28:38 +02:00
_memberRepository . Save ( member ) ;
}
2017-12-28 09:18:09 +01:00
2022-06-07 15:28:38 +02:00
scope . Notifications . Publish ( new MemberSavedNotification ( membersA , evtMsgs ) . WithStateFrom ( savingNotification ) ) ;
2021-07-12 09:46:56 -06:00
2024-02-05 06:42:07 +01:00
Audit ( AuditType . Save , userId , Constants . System . Root , "Save multiple Members" ) ;
2017-12-28 09:18:09 +01:00
2022-06-07 15:28:38 +02:00
scope . Complete ( ) ;
2024-02-05 06:42:07 +01:00
return OperationResult . Attempt . Succeed ( evtMsgs ) ;
2017-12-28 09:18:09 +01:00
}
2024-02-05 06:42:07 +01:00
[Obsolete($"Use the {nameof(Save)} method that yields an Attempt. Will be removed in V15.")]
public void Save ( IEnumerable < IMember > members )
= > Save ( members , Constants . Security . SuperUserId ) ;
2017-12-28 09:18:09 +01:00
#endregion
#region Delete
2024-02-05 06:42:07 +01:00
/// <inheritdoc />
public Attempt < OperationResult ? > Delete ( IMember member , int userId = Constants . Security . SuperUserId )
2017-12-28 09:18:09 +01:00
{
2022-06-07 15:28:38 +02:00
EventMessages evtMsgs = EventMessagesFactory . Get ( ) ;
2021-03-09 07:52:32 +01:00
2022-06-07 15:28:38 +02:00
using ICoreScope scope = ScopeProvider . CreateCoreScope ( ) ;
var deletingNotification = new MemberDeletingNotification ( member , evtMsgs ) ;
if ( scope . Notifications . PublishCancelable ( deletingNotification ) )
2017-12-28 09:18:09 +01:00
{
scope . Complete ( ) ;
2024-02-05 06:42:07 +01:00
return OperationResult . Attempt . Cancel ( evtMsgs ) ;
2017-12-28 09:18:09 +01:00
}
2022-06-07 15:28:38 +02:00
scope . WriteLock ( Constants . Locks . MemberTree ) ;
DeleteLocked ( scope , member , evtMsgs , deletingNotification . State ) ;
Audit ( AuditType . Delete , 0 , member . Id ) ;
scope . Complete ( ) ;
2024-02-05 06:42:07 +01:00
return OperationResult . Attempt . Succeed ( evtMsgs ) ;
2017-12-28 09:18:09 +01:00
}
2024-02-05 06:42:07 +01:00
/// <inheritdoc />
public void Delete ( IMember member )
= > Delete ( member , Constants . Security . SuperUserId ) ;
2022-05-01 08:18:09 +02:00
private void DeleteLocked ( ICoreScope scope , IMember member , EventMessages evtMsgs , IDictionary < string , object? > ? notificationState = null )
2017-12-28 09:18:09 +01:00
{
// a member has no descendants
_memberRepository . Delete ( member ) ;
2021-03-16 09:21:27 +01:00
scope . Notifications . Publish ( new MemberDeletedNotification ( member , evtMsgs ) . WithState ( notificationState ) ) ;
2017-12-28 09:18:09 +01:00
2018-10-26 15:06:53 +02:00
// media files deleted by QueuingEventDispatcher
2017-12-28 09:18:09 +01:00
}
#endregion
#region Roles
public void AddRole ( string roleName )
{
2022-06-07 15:28:38 +02:00
using ICoreScope scope = ScopeProvider . CreateCoreScope ( ) ;
scope . WriteLock ( Constants . Locks . MemberTree ) ;
_memberGroupRepository . CreateIfNotExists ( roleName ) ;
scope . Complete ( ) ;
2017-12-28 09:18:09 +01:00
}
2021-01-11 17:01:44 +00:00
2020-12-08 17:18:22 +00:00
/// <summary>
2021-01-11 17:01:44 +00:00
/// Returns a list of all member roles
2020-12-08 17:18:22 +00:00
/// </summary>
2021-01-11 17:01:44 +00:00
/// <returns>A list of member roles</returns>
2020-12-08 17:18:22 +00:00
2021-01-11 17:01:44 +00:00
public IEnumerable < IMemberGroup > GetAllRoles ( )
2020-12-08 17:18:22 +00:00
{
2022-06-07 15:28:38 +02:00
using ICoreScope scope = ScopeProvider . CreateCoreScope ( autoComplete : true ) ;
scope . ReadLock ( Constants . Locks . MemberTree ) ;
return _memberGroupRepository . GetMany ( ) . Distinct ( ) ;
2020-12-08 17:18:22 +00:00
}
2021-01-11 17:01:44 +00:00
/// <summary>
/// Returns a list of all member roles for a given member ID
/// </summary>
/// <param name="memberId"></param>
/// <returns>A list of member roles</returns>
2022-05-06 10:13:58 +02:00
public IEnumerable < string > GetAllRoles ( int memberId )
2017-12-28 09:18:09 +01:00
{
2022-06-07 15:28:38 +02:00
using ICoreScope scope = ScopeProvider . CreateCoreScope ( autoComplete : true ) ;
scope . ReadLock ( Constants . Locks . MemberTree ) ;
IEnumerable < IMemberGroup > result = _memberGroupRepository . GetMemberGroupsForMember ( memberId ) ;
return result . Select ( x = > x . Name ) . WhereNotNull ( ) . Distinct ( ) ;
2017-12-28 09:18:09 +01:00
}
2022-05-06 10:13:58 +02:00
public IEnumerable < string > GetAllRoles ( string username )
2017-12-28 09:18:09 +01:00
{
2022-06-07 15:28:38 +02:00
using ICoreScope scope = ScopeProvider . CreateCoreScope ( autoComplete : true ) ;
scope . ReadLock ( Constants . Locks . MemberTree ) ;
IEnumerable < IMemberGroup > result = _memberGroupRepository . GetMemberGroupsForMember ( username ) ;
return result . Where ( x = > x . Name ! = null ) . Select ( x = > x . Name ) . Distinct ( ) ! ;
2017-12-28 09:18:09 +01:00
}
2020-03-24 14:15:52 +00:00
public IEnumerable < int > GetAllRolesIds ( )
{
2022-06-07 15:28:38 +02:00
using ICoreScope scope = ScopeProvider . CreateCoreScope ( autoComplete : true ) ;
scope . ReadLock ( Constants . Locks . MemberTree ) ;
return _memberGroupRepository . GetMany ( ) . Select ( x = > x . Id ) . Distinct ( ) ;
2020-03-24 14:15:52 +00:00
}
public IEnumerable < int > GetAllRolesIds ( int memberId )
{
2022-06-07 15:28:38 +02:00
using ICoreScope scope = ScopeProvider . CreateCoreScope ( autoComplete : true ) ;
scope . ReadLock ( Constants . Locks . MemberTree ) ;
IEnumerable < IMemberGroup > result = _memberGroupRepository . GetMemberGroupsForMember ( memberId ) ;
return result . Select ( x = > x . Id ) . Distinct ( ) ;
2020-03-24 14:15:52 +00:00
}
public IEnumerable < int > GetAllRolesIds ( string username )
{
2022-06-07 15:28:38 +02:00
using ICoreScope scope = ScopeProvider . CreateCoreScope ( autoComplete : true ) ;
scope . ReadLock ( Constants . Locks . MemberTree ) ;
IEnumerable < IMemberGroup > result = _memberGroupRepository . GetMemberGroupsForMember ( username ) ;
return result . Select ( x = > x . Id ) . Distinct ( ) ;
2020-03-24 14:15:52 +00:00
}
2020-09-18 15:27:38 +02:00
2017-12-28 09:18:09 +01:00
public IEnumerable < IMember > GetMembersInRole ( string roleName )
{
2022-06-07 15:28:38 +02:00
using ICoreScope scope = ScopeProvider . CreateCoreScope ( autoComplete : true ) ;
scope . ReadLock ( Constants . Locks . MemberTree ) ;
return _memberRepository . GetByMemberGroup ( roleName ) ;
2017-12-28 09:18:09 +01:00
}
public IEnumerable < IMember > FindMembersInRole ( string roleName , string usernameToMatch , StringPropertyMatchType matchType = StringPropertyMatchType . StartsWith )
{
2022-06-07 15:28:38 +02:00
using ICoreScope scope = ScopeProvider . CreateCoreScope ( autoComplete : true ) ;
scope . ReadLock ( Constants . Locks . MemberTree ) ;
return _memberRepository . FindMembersInRole ( roleName , usernameToMatch , matchType ) ;
2017-12-28 09:18:09 +01:00
}
public bool DeleteRole ( string roleName , bool throwIfBeingUsed )
{
2022-06-07 15:28:38 +02:00
using ICoreScope scope = ScopeProvider . CreateCoreScope ( ) ;
scope . WriteLock ( Constants . Locks . MemberTree ) ;
2017-12-28 09:18:09 +01:00
2022-06-07 15:28:38 +02:00
if ( throwIfBeingUsed )
{
// get members in role
IEnumerable < IMember > membersInRole = _memberRepository . GetByMemberGroup ( roleName ) ;
if ( membersInRole . Any ( ) )
2017-12-28 09:18:09 +01:00
{
2022-06-07 15:28:38 +02:00
throw new InvalidOperationException ( "The role " + roleName + " is currently assigned to members" ) ;
2017-12-28 09:18:09 +01:00
}
2022-06-07 15:28:38 +02:00
}
2017-12-28 09:18:09 +01:00
2022-06-07 15:28:38 +02:00
IQuery < IMemberGroup > query = Query < IMemberGroup > ( ) . Where ( g = > g . Name = = roleName ) ;
IMemberGroup [ ] ? found = _memberGroupRepository . Get ( query ) ? . ToArray ( ) ;
2017-12-28 09:18:09 +01:00
2022-06-07 15:28:38 +02:00
if ( found is not null )
{
foreach ( IMemberGroup memberGroup in found )
2021-01-11 17:01:44 +00:00
{
2022-06-07 15:28:38 +02:00
_memberGroupService . Delete ( memberGroup ) ;
2021-01-11 17:01:44 +00:00
}
2017-12-28 09:18:09 +01:00
}
2022-06-07 15:28:38 +02:00
scope . Complete ( ) ;
return found ? . Length > 0 ;
2017-12-28 09:18:09 +01:00
}
2021-01-11 17:01:44 +00:00
public void AssignRole ( string username , string roleName ) = > AssignRoles ( new [ ] { username } , new [ ] { roleName } ) ;
2017-12-28 09:18:09 +01:00
public void AssignRoles ( string [ ] usernames , string [ ] roleNames )
{
2022-06-07 15:28:38 +02:00
using ICoreScope scope = ScopeProvider . CreateCoreScope ( ) ;
scope . WriteLock ( Constants . Locks . MemberTree ) ;
var ids = _memberRepository . GetMemberIds ( usernames ) ;
_memberGroupRepository . AssignRoles ( ids , roleNames ) ;
scope . Notifications . Publish ( new AssignedMemberRolesNotification ( ids , roleNames ) ) ;
scope . Complete ( ) ;
2017-12-28 09:18:09 +01:00
}
2021-01-11 17:01:44 +00:00
public void DissociateRole ( string username , string roleName ) = > DissociateRoles ( new [ ] { username } , new [ ] { roleName } ) ;
2017-12-28 09:18:09 +01:00
public void DissociateRoles ( string [ ] usernames , string [ ] roleNames )
{
2022-06-07 15:28:38 +02:00
using ICoreScope scope = ScopeProvider . CreateCoreScope ( ) ;
scope . WriteLock ( Constants . Locks . MemberTree ) ;
var ids = _memberRepository . GetMemberIds ( usernames ) ;
_memberGroupRepository . DissociateRoles ( ids , roleNames ) ;
scope . Notifications . Publish ( new RemovedMemberRolesNotification ( ids , roleNames ) ) ;
scope . Complete ( ) ;
2017-12-28 09:18:09 +01:00
}
2021-01-11 17:01:44 +00:00
public void AssignRole ( int memberId , string roleName ) = > AssignRoles ( new [ ] { memberId } , new [ ] { roleName } ) ;
2017-12-28 09:18:09 +01:00
public void AssignRoles ( int [ ] memberIds , string [ ] roleNames )
{
2022-06-07 15:28:38 +02:00
using ICoreScope scope = ScopeProvider . CreateCoreScope ( ) ;
scope . WriteLock ( Constants . Locks . MemberTree ) ;
_memberGroupRepository . AssignRoles ( memberIds , roleNames ) ;
scope . Notifications . Publish ( new AssignedMemberRolesNotification ( memberIds , roleNames ) ) ;
scope . Complete ( ) ;
2017-12-28 09:18:09 +01:00
}
2021-01-11 17:01:44 +00:00
public void DissociateRole ( int memberId , string roleName ) = > DissociateRoles ( new [ ] { memberId } , new [ ] { roleName } ) ;
2017-12-28 09:18:09 +01:00
public void DissociateRoles ( int [ ] memberIds , string [ ] roleNames )
{
2022-06-07 15:28:38 +02:00
using ICoreScope scope = ScopeProvider . CreateCoreScope ( ) ;
scope . WriteLock ( Constants . Locks . MemberTree ) ;
_memberGroupRepository . DissociateRoles ( memberIds , roleNames ) ;
scope . Notifications . Publish ( new RemovedMemberRolesNotification ( memberIds , roleNames ) ) ;
scope . Complete ( ) ;
2017-12-28 09:18:09 +01:00
}
Security stamp implementation for members (#10140)
* Getting new netcore PublicAccessChecker in place
* Adds full test coverage for PublicAccessChecker
* remove PublicAccessComposer
* adjust namespaces, ensure RoleManager works, separate public access controller, reduce content controller
* Implements the required methods on IMemberManager, removes old migrated code
* Updates routing to be able to re-route, Fixes middleware ordering ensuring endpoints are last, refactors pipeline options, adds public access middleware, ensures public access follows all hops
* adds note
* adds note
* Cleans up ext methods, ensures that members identity is added on both front-end and back ends. updates how UmbracoApplicationBuilder works in that it explicitly starts endpoints at the time of calling.
* Changes name to IUmbracoEndpointBuilder
* adds note
* Fixing tests, fixing error describers so there's 2x one for back office, one for members, fixes TryConvertTo, fixes login redirect
* fixing build
* Updates user manager to correctly validate password hashing and injects the IBackOfficeUserPasswordChecker
* Merges PR
* Fixes up build and notes
* Implements security stamp and email confirmed for members, cleans up a bunch of repo/service level member groups stuff, shares user store code between members and users and fixes the user identity object so we arent' tracking both groups and roles.
* Security stamp for members is now working
* Fixes keepalive, fixes PublicAccessMiddleware to not throw, updates startup code to be more clear and removes magic that registers middleware.
* adds note
* removes unused filter, fixes build
* fixes WebPath and tests
* Looks up entities in one query
* remove usings
* Fix test, remove stylesheet
* Set status code before we write to response to avoid error
* Ensures that users and members are validated when logging in. Shares more code between users and members.
* merge changes
* oops
* Fixes RepositoryCacheKeys to ensure the keys are normalized
* oops didn't mean to commit this
* Fix casing issues with caching, stop boxing value types for all cache operations, stop re-creating string keys in DefaultRepositoryCachePolicy
* oops didn't mean to comit this
* bah, far out this keeps getting recommitted. sorry
Co-authored-by: Bjarke Berg <mail@bergmania.dk>
2021-04-20 17:13:40 +10:00
public void ReplaceRoles ( string [ ] usernames , string [ ] roleNames )
{
2022-06-07 15:28:38 +02:00
using ICoreScope scope = ScopeProvider . CreateCoreScope ( ) ;
scope . WriteLock ( Constants . Locks . MemberTree ) ;
int [ ] ids = _memberRepository . GetMemberIds ( usernames ) ;
_memberGroupRepository . ReplaceRoles ( ids , roleNames ) ;
scope . Notifications . Publish ( new AssignedMemberRolesNotification ( ids , roleNames ) ) ;
scope . Complete ( ) ;
Security stamp implementation for members (#10140)
* Getting new netcore PublicAccessChecker in place
* Adds full test coverage for PublicAccessChecker
* remove PublicAccessComposer
* adjust namespaces, ensure RoleManager works, separate public access controller, reduce content controller
* Implements the required methods on IMemberManager, removes old migrated code
* Updates routing to be able to re-route, Fixes middleware ordering ensuring endpoints are last, refactors pipeline options, adds public access middleware, ensures public access follows all hops
* adds note
* adds note
* Cleans up ext methods, ensures that members identity is added on both front-end and back ends. updates how UmbracoApplicationBuilder works in that it explicitly starts endpoints at the time of calling.
* Changes name to IUmbracoEndpointBuilder
* adds note
* Fixing tests, fixing error describers so there's 2x one for back office, one for members, fixes TryConvertTo, fixes login redirect
* fixing build
* Updates user manager to correctly validate password hashing and injects the IBackOfficeUserPasswordChecker
* Merges PR
* Fixes up build and notes
* Implements security stamp and email confirmed for members, cleans up a bunch of repo/service level member groups stuff, shares user store code between members and users and fixes the user identity object so we arent' tracking both groups and roles.
* Security stamp for members is now working
* Fixes keepalive, fixes PublicAccessMiddleware to not throw, updates startup code to be more clear and removes magic that registers middleware.
* adds note
* removes unused filter, fixes build
* fixes WebPath and tests
* Looks up entities in one query
* remove usings
* Fix test, remove stylesheet
* Set status code before we write to response to avoid error
* Ensures that users and members are validated when logging in. Shares more code between users and members.
* merge changes
* oops
* Fixes RepositoryCacheKeys to ensure the keys are normalized
* oops didn't mean to commit this
* Fix casing issues with caching, stop boxing value types for all cache operations, stop re-creating string keys in DefaultRepositoryCachePolicy
* oops didn't mean to comit this
* bah, far out this keeps getting recommitted. sorry
Co-authored-by: Bjarke Berg <mail@bergmania.dk>
2021-04-20 17:13:40 +10:00
}
public void ReplaceRoles ( int [ ] memberIds , string [ ] roleNames )
{
2022-06-07 15:28:38 +02:00
using ICoreScope scope = ScopeProvider . CreateCoreScope ( ) ;
scope . WriteLock ( Constants . Locks . MemberTree ) ;
_memberGroupRepository . ReplaceRoles ( memberIds , roleNames ) ;
scope . Notifications . Publish ( new AssignedMemberRolesNotification ( memberIds , roleNames ) ) ;
scope . Complete ( ) ;
Security stamp implementation for members (#10140)
* Getting new netcore PublicAccessChecker in place
* Adds full test coverage for PublicAccessChecker
* remove PublicAccessComposer
* adjust namespaces, ensure RoleManager works, separate public access controller, reduce content controller
* Implements the required methods on IMemberManager, removes old migrated code
* Updates routing to be able to re-route, Fixes middleware ordering ensuring endpoints are last, refactors pipeline options, adds public access middleware, ensures public access follows all hops
* adds note
* adds note
* Cleans up ext methods, ensures that members identity is added on both front-end and back ends. updates how UmbracoApplicationBuilder works in that it explicitly starts endpoints at the time of calling.
* Changes name to IUmbracoEndpointBuilder
* adds note
* Fixing tests, fixing error describers so there's 2x one for back office, one for members, fixes TryConvertTo, fixes login redirect
* fixing build
* Updates user manager to correctly validate password hashing and injects the IBackOfficeUserPasswordChecker
* Merges PR
* Fixes up build and notes
* Implements security stamp and email confirmed for members, cleans up a bunch of repo/service level member groups stuff, shares user store code between members and users and fixes the user identity object so we arent' tracking both groups and roles.
* Security stamp for members is now working
* Fixes keepalive, fixes PublicAccessMiddleware to not throw, updates startup code to be more clear and removes magic that registers middleware.
* adds note
* removes unused filter, fixes build
* fixes WebPath and tests
* Looks up entities in one query
* remove usings
* Fix test, remove stylesheet
* Set status code before we write to response to avoid error
* Ensures that users and members are validated when logging in. Shares more code between users and members.
* merge changes
* oops
* Fixes RepositoryCacheKeys to ensure the keys are normalized
* oops didn't mean to commit this
* Fix casing issues with caching, stop boxing value types for all cache operations, stop re-creating string keys in DefaultRepositoryCachePolicy
* oops didn't mean to comit this
* bah, far out this keeps getting recommitted. sorry
Co-authored-by: Bjarke Berg <mail@bergmania.dk>
2021-04-20 17:13:40 +10:00
}
2017-12-28 09:18:09 +01:00
#endregion
2024-02-05 06:42:07 +01:00
#region Others
// NOTE: at the time of writing we do not have MemberTreeChangeNotification to publish changes as a result of a data integrity
// check. we cannot support this feature until such notification exists.
// see the content or media services for implementation details if this is ever going to be a relevant feature for members.
public ContentDataIntegrityReport CheckDataIntegrity ( ContentDataIntegrityReportOptions options )
= > throw new InvalidOperationException ( "Data integrity checks are not (yet) implemented for members." ) ;
#endregion
2017-12-28 09:18:09 +01:00
#region Private Methods
2022-02-16 16:03:53 +01:00
private void Audit ( AuditType type , int userId , int objectId , string? message = null ) = > _auditRepository . Save ( new AuditItem ( objectId , type , userId , ObjectTypes . GetName ( UmbracoObjectTypes . Member ) , message ) ) ;
2017-12-28 09:18:09 +01:00
2024-04-11 13:53:34 +02:00
private IMember ? GetMemberFromRepository ( Guid id )
= > _idKeyMap . Value . GetIdForKey ( id , UmbracoObjectTypes . Member ) switch
{
{ Success : false } = > null ,
{ Result : var intId } = > _memberRepository . Get ( intId ) ,
} ;
2017-12-28 09:18:09 +01:00
#endregion
#region Membership
2018-03-22 17:41:13 +01:00
/// <summary>
/// Exports a member.
/// </summary>
/// <remarks>
2019-11-05 13:45:42 +01:00
/// This is internal for now and is used to export a member in the member editor,
2018-03-22 17:41:13 +01:00
/// it will raise an event so that auditing logs can be created.
/// </remarks>
2022-02-16 16:03:53 +01:00
public MemberExportModel ? ExportMember ( Guid key )
2018-03-22 17:41:13 +01:00
{
2022-06-07 15:28:38 +02:00
using ICoreScope scope = ScopeProvider . CreateCoreScope ( autoComplete : true ) ;
IQuery < IMember > ? query = Query < IMember > ( ) . Where ( x = > x . Key = = key ) ;
IMember ? member = _memberRepository . Get ( query ) ? . FirstOrDefault ( ) ;
2018-03-22 17:41:13 +01:00
2022-06-07 15:28:38 +02:00
if ( member = = null )
{
return null ;
2018-03-22 17:41:13 +01:00
}
2022-06-07 15:28:38 +02:00
var model = new MemberExportModel
{
Id = member . Id ,
Key = member . Key ,
Name = member . Name ,
Username = member . Username ,
Email = member . Email ,
Groups = GetAllRoles ( member . Id ) . ToList ( ) ,
ContentTypeAlias = member . ContentTypeAlias ,
CreateDate = member . CreateDate ,
UpdateDate = member . UpdateDate ,
Properties = new List < MemberExportProperty > ( GetPropertyExportItems ( member ) )
} ;
scope . Notifications . Publish ( new ExportedMemberNotification ( member , model ) ) ;
return model ;
2018-03-22 17:41:13 +01:00
}
private static IEnumerable < MemberExportProperty > GetPropertyExportItems ( IMember member )
{
2021-01-11 17:01:44 +00:00
if ( member = = null )
{
throw new ArgumentNullException ( nameof ( member ) ) ;
}
2018-03-22 17:41:13 +01:00
var exportProperties = new List < MemberExportProperty > ( ) ;
2021-01-11 17:01:44 +00:00
foreach ( IProperty property in member . Properties )
2018-03-22 17:41:13 +01:00
{
var propertyExportModel = new MemberExportProperty
{
Id = property . Id ,
Alias = property . Alias ,
Name = property . PropertyType . Name ,
2019-01-26 09:42:14 -05:00
Value = property . GetValue ( ) , // TODO: ignoring variants
2018-03-22 17:41:13 +01:00
CreateDate = property . CreateDate ,
UpdateDate = property . UpdateDate
} ;
exportProperties . Add ( propertyExportModel ) ;
}
return exportProperties ;
}
2017-12-28 09:18:09 +01:00
#endregion
#region Content Types
/// <summary>
/// Delete Members of the specified MemberType id
/// </summary>
/// <param name="memberTypeId">Id of the MemberType</param>
public void DeleteMembersOfType ( int memberTypeId )
{
2022-06-07 15:28:38 +02:00
EventMessages evtMsgs = EventMessagesFactory . Get ( ) ;
2021-03-09 07:52:32 +01:00
2017-12-28 09:18:09 +01:00
// note: no tree to manage here
2022-06-07 15:28:38 +02:00
using ICoreScope scope = ScopeProvider . CreateCoreScope ( ) ;
scope . WriteLock ( Constants . Locks . MemberTree ) ;
2017-12-28 09:18:09 +01:00
2022-06-07 15:28:38 +02:00
// TODO: What about content that has the contenttype as part of its composition?
IQuery < IMember > ? query = Query < IMember > ( ) . Where ( x = > x . ContentTypeId = = memberTypeId ) ;
2022-02-28 13:14:02 +01:00
2022-06-07 15:28:38 +02:00
IMember [ ] ? members = _memberRepository . Get ( query ) ? . ToArray ( ) ;
2017-12-28 09:18:09 +01:00
2022-06-07 15:28:38 +02:00
if ( members is null )
{
return ;
}
2021-01-11 17:01:44 +00:00
2022-06-07 15:28:38 +02:00
if ( scope . Notifications . PublishCancelable ( new MemberDeletingNotification ( members , evtMsgs ) ) )
{
2017-12-28 09:18:09 +01:00
scope . Complete ( ) ;
2022-06-07 15:28:38 +02:00
return ;
2017-12-28 09:18:09 +01:00
}
2022-06-07 15:28:38 +02:00
foreach ( IMember member in members )
{
// delete media
// triggers the deleted event (and handles the files)
DeleteLocked ( scope , member , evtMsgs ) ;
}
scope . Complete ( ) ;
2017-12-28 09:18:09 +01:00
}
2022-04-26 10:22:37 +01:00
private IMemberType GetMemberType ( ICoreScope scope , string memberTypeAlias )
2017-12-28 09:18:09 +01:00
{
2021-01-11 17:01:44 +00:00
if ( memberTypeAlias = = null )
{
throw new ArgumentNullException ( nameof ( memberTypeAlias ) ) ;
}
if ( string . IsNullOrWhiteSpace ( memberTypeAlias ) )
{
throw new ArgumentException ( "Value can't be empty or consist only of white-space characters." , nameof ( memberTypeAlias ) ) ;
}
2017-12-28 09:18:09 +01:00
scope . ReadLock ( Constants . Locks . MemberTypes ) ;
2022-02-28 13:14:02 +01:00
IMemberType ? memberType = _memberTypeRepository . Get ( memberTypeAlias ) ;
2017-12-28 09:18:09 +01:00
if ( memberType = = null )
2021-01-11 17:01:44 +00:00
{
2017-12-28 09:18:09 +01:00
throw new Exception ( $"No MemberType matching the passed in Alias: '{memberTypeAlias}' was found" ) ; // causes rollback
2021-01-11 17:01:44 +00:00
}
2017-12-28 09:18:09 +01:00
return memberType ;
}
private IMemberType GetMemberType ( string memberTypeAlias )
{
2021-01-11 17:01:44 +00:00
if ( memberTypeAlias = = null )
{
throw new ArgumentNullException ( nameof ( memberTypeAlias ) ) ;
}
2017-12-28 09:18:09 +01:00
2021-01-11 17:01:44 +00:00
if ( string . IsNullOrWhiteSpace ( memberTypeAlias ) )
{
throw new ArgumentException ( "Value can't be empty or consist only of white-space characters." , nameof ( memberTypeAlias ) ) ;
}
2022-06-07 15:28:38 +02:00
using ICoreScope scope = ScopeProvider . CreateCoreScope ( autoComplete : true ) ;
return GetMemberType ( scope , memberTypeAlias ) ;
2017-12-28 09:18:09 +01:00
}
#endregion
}
}