using System;
using System.Collections.Generic;
using System.Web.Security;
using Umbraco.Core.Configuration;
using Umbraco.Core.Events;
using Umbraco.Core.Logging;
using Umbraco.Core.Models;
using Umbraco.Core.Models.Membership;
using Umbraco.Core.Persistence.DatabaseModelDefinitions;
using Umbraco.Core.Persistence.Querying;
using Umbraco.Core.Persistence.UnitOfWork;
using System.Linq;
using Umbraco.Core.IO;
using Umbraco.Core.Persistence.Repositories;
using Umbraco.Core.Security;
namespace Umbraco.Core.Services
{
///
/// Represents the MemberService.
///
public class MemberService : RepositoryService, IMemberService
{
private readonly EntityXmlSerializer _entitySerializer = new EntityXmlSerializer();
private readonly IDataTypeService _dataTypeService;
private readonly IMemberGroupService _memberGroupService;
private IMemberTypeService _memberTypeService;
#region Constructor
public MemberService(
IDatabaseUnitOfWorkProvider provider,
ILogger logger,
IEventMessagesFactory eventMessagesFactory,
IMemberGroupService memberGroupService,
IDataTypeService dataTypeService)
: base(provider, logger, eventMessagesFactory)
{
if (memberGroupService == null) throw new ArgumentNullException(nameof(memberGroupService));
if (dataTypeService == null) throw new ArgumentNullException(nameof(dataTypeService));
_memberGroupService = memberGroupService;
_dataTypeService = dataTypeService;
}
// don't change or remove this, will need it later
private IMemberTypeService MemberTypeService => _memberTypeService;
//// handle circular dependencies
//internal IMemberTypeService MemberTypeService
//{
// get
// {
// if (_memberTypeService == null)
// throw new InvalidOperationException("MemberService.MemberTypeService has not been initialized.");
// return _memberTypeService;
// }
// set { _memberTypeService = value; }
//}
#endregion
#region Count
///
/// Gets the total number of Members based on the count type
///
///
/// 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.
///
/// to count by
/// with number of Members for passed in type
public int GetCount(MemberCountType countType)
{
using (var uow = UowProvider.CreateUnitOfWork())
{
uow.ReadLock(Constants.Locks.MemberTree);
var repository = uow.CreateRepository();
IQuery query;
switch (countType)
{
case MemberCountType.All:
query = repository.Query;
break;
case MemberCountType.Online:
var fromDate = DateTime.Now.AddMinutes(-Membership.UserIsOnlineTimeWindow);
query = repository.Query.Where(x =>
((Member)x).PropertyTypeAlias == Constants.Conventions.Member.LastLoginDate &&
((Member)x).DateTimePropertyValue > fromDate);
break;
case MemberCountType.LockedOut:
query = repository.Query.Where(x =>
((Member)x).PropertyTypeAlias == Constants.Conventions.Member.IsLockedOut &&
((Member)x).BoolPropertyValue);
break;
case MemberCountType.Approved:
query = repository.Query.Where(x =>
((Member)x).PropertyTypeAlias == Constants.Conventions.Member.IsApproved &&
((Member)x).BoolPropertyValue);
break;
default:
throw new ArgumentOutOfRangeException(nameof(countType)); // causes rollback;
}
var count = repository.GetCountByQuery(query);
uow.Complete();
return count;
}
}
///
/// Gets the count of Members by an optional MemberType alias
///
/// If no alias is supplied then the count for all Member will be returned
/// Optional alias for the MemberType when counting number of Members
/// with number of Members
public int Count(string memberTypeAlias = null)
{
using (var uow = UowProvider.CreateUnitOfWork())
{
uow.ReadLock(Constants.Locks.MemberTree);
var repository = uow.CreateRepository();
var count = repository.Count(memberTypeAlias);
uow.Complete();
return count;
}
}
#endregion
#region Create
///
/// Creates an object without persisting it
///
/// 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 will not have an Id until its saved.
/// Username of the Member to create
/// Email of the Member to create
/// Name of the Member to create
/// Alias of the MemberType the Member should be based on
///
public IMember CreateMember(string username, string email, string name, string memberTypeAlias)
{
var memberType = GetMemberType(memberTypeAlias);
if (memberType == null)
throw new ArgumentException("No member type with that alias.", nameof(memberTypeAlias));
var member = new Member(name, email.ToLower().Trim(), username, memberType);
CreateMember(null, member, 0, false);
return member;
}
///
/// Creates an object without persisting it
///
/// 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 will not have an Id until its saved.
/// Username of the Member to create
/// Email of the Member to create
/// Name of the Member to create
/// MemberType the Member should be based on
///
public IMember CreateMember(string username, string email, string name, IMemberType memberType)
{
if (memberType == null) throw new ArgumentNullException(nameof(memberType));
var member = new Member(name, email.ToLower().Trim(), username, memberType);
CreateMember(null, member, 0, false);
return member;
}
///
/// Creates and persists a Member
///
/// Using this method will persist the Member object before its returned
/// meaning that it will have an Id available (unlike the CreateMember method)
/// Username of the Member to create
/// Email of the Member to create
/// Name of the Member to create
/// Alias of the MemberType the Member should be based on
///
public IMember CreateMemberWithIdentity(string username, string email, string name, string memberTypeAlias)
{
using (var uow = UowProvider.CreateUnitOfWork())
{
// locking the media tree secures media types too
uow.WriteLock(Constants.Locks.MediaTree);
var memberType = GetMemberType(memberTypeAlias); // + locks
if (memberType == null)
throw new ArgumentException("No member type with that alias.", nameof(memberTypeAlias)); // causes rollback
var member = new Member(name, email.ToLower().Trim(), username, memberType);
CreateMember(uow, member, 0, true);
uow.Complete();
return member;
}
}
///
/// Creates and persists a Member
///
/// Using this method will persist the Member object before its returned
/// meaning that it will have an Id available (unlike the CreateMember method)
/// Username of the Member to create
/// Email of the Member to create
/// MemberType the Member should be based on
///
public IMember CreateMemberWithIdentity(string username, string email, IMemberType memberType)
{
return CreateMemberWithIdentity(username, email, username, "", memberType);
}
///
/// Creates and persists a Member
///
/// Using this method will persist the Member object before its returned
/// meaning that it will have an Id available (unlike the CreateMember method)
/// Username of the Member to create
/// Email of the Member to create
/// Name of the Member to create
/// MemberType the Member should be based on
///
public IMember CreateMemberWithIdentity(string username, string email, string name, IMemberType memberType)
{
return CreateMemberWithIdentity(username, email, name, "", memberType);
}
///
/// Creates and persists a new
///
/// An can be of type or
/// Username of the to create
/// Email of the to create
/// This value should be the encoded/encrypted/hashed value for the password that will be stored in the database
/// Alias of the Type
///
IMember IMembershipMemberService.CreateWithIdentity(string username, string email, string passwordValue, string memberTypeAlias)
{
using (var uow = UowProvider.CreateUnitOfWork())
{
uow.WriteLock(Constants.Locks.MediaTree);
// ensure it all still make sense
var memberType = GetMemberType(memberTypeAlias); // + locks
if (memberType == null)
throw new ArgumentException("No member type with that alias.", nameof(memberTypeAlias)); // causes rollback
var member = new Member(username, email.ToLower().Trim(), username, passwordValue, memberType);
CreateMember(uow, member, -1, true);
uow.Complete();
return member;
}
}
///
/// Creates and persists a Member
///
/// Using this method will persist the Member object before its returned
/// meaning that it will have an Id available (unlike the CreateMember method)
/// Username of the Member to create
/// Email of the Member to create
/// Name of the Member to create
/// This value should be the encoded/encrypted/hashed value for the password that will be stored in the database
/// MemberType the Member should be based on
///
private IMember CreateMemberWithIdentity(string username, string email, string name, string passwordValue, IMemberType memberType)
{
if (memberType == null) throw new ArgumentNullException(nameof(memberType));
using (var uow = UowProvider.CreateUnitOfWork())
{
uow.WriteLock(Constants.Locks.MediaTree);
// ensure it all still make sense
var vrfy = GetMemberType(memberType.Alias); // + locks
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
var member = new Member(name, email.ToLower().Trim(), username, passwordValue, memberType);
CreateMember(uow, member, -1, true);
uow.Complete();
return member;
}
}
private void CreateMember(IDatabaseUnitOfWork uow, Member member, int userId, bool withIdentity)
{
// there's no Creating event for members
if (Saving.IsRaisedEventCancelled(new SaveEventArgs(member), this))
{
member.WasCancelled = true;
return;
}
member.CreatorId = userId;
if (withIdentity)
{
if (Saving.IsRaisedEventCancelled(new SaveEventArgs(member), this))
{
member.WasCancelled = true;
return;
}
var repository = uow.CreateRepository();
repository.AddOrUpdate(member);
// fixme kill
repository.AddOrUpdateContentXml(member, m => _entitySerializer.Serialize(_dataTypeService, m));
// generate preview for blame history?
if (UmbracoConfig.For.UmbracoSettings().Content.GlobalPreviewStorageEnabled)
repository.AddOrUpdatePreviewXml(member, m => _entitySerializer.Serialize(_dataTypeService, m));
Saved.RaiseEvent(new SaveEventArgs(member, false), this);
}
Created.RaiseEvent(new NewEventArgs(member, false, member.ContentType.Alias, -1), this);
var msg = withIdentity
? "Member '{0}' was created with Id {1}"
: "Member '{0}' was created";
Audit(AuditType.New, string.Format(msg, member.Name, member.Id), member.CreatorId, member.Id);
}
#endregion
#region Get, Has, Is, Exists...
///
/// Gets a Member by its integer id
///
/// Id
///
public IMember GetById(int id)
{
using (var uow = UowProvider.CreateUnitOfWork())
{
uow.ReadLock(Constants.Locks.MemberTree);
var repository = uow.CreateRepository();
var member = repository.Get(id);
uow.Complete();
return member;
}
}
///
/// Gets a Member by the unique key
///
/// The guid key corresponds to the unique id in the database
/// and the user id in the membership provider.
/// Id
///
public IMember GetByKey(Guid id)
{
using (var uow = UowProvider.CreateUnitOfWork())
{
uow.ReadLock(Constants.Locks.MemberTree);
var repository = uow.CreateRepository();
var query = repository.Query.Where(x => x.Key == id);
var member = repository.GetByQuery(query).FirstOrDefault();
uow.Complete();
return member;
}
}
///
/// Gets a list of paged objects
///
/// Current page index
/// Size of the page
/// Total number of records found (out)
///
public IEnumerable GetAll(long pageIndex, int pageSize, out long totalRecords)
{
using (var uow = UowProvider.CreateUnitOfWork())
{
uow.ReadLock(Constants.Locks.MemberTree);
var repository = uow.CreateRepository();
var members = repository.GetPagedResultsByQuery(null, pageIndex, pageSize, out totalRecords, "LoginName", Direction.Ascending, true);
uow.Complete();
return members;
}
}
public IEnumerable GetAll(long pageIndex, int pageSize, out long totalRecords,
string orderBy, Direction orderDirection, string memberTypeAlias = null, string filter = "")
{
return GetAll(pageIndex, pageSize, out totalRecords, orderBy, orderDirection, true, memberTypeAlias, filter);
}
public IEnumerable GetAll(long pageIndex, int pageSize, out long totalRecords,
string orderBy, Direction orderDirection, bool orderBySystemField, string memberTypeAlias, string filter)
{
using (var uow = UowProvider.CreateUnitOfWork())
{
uow.ReadLock(Constants.Locks.MemberTree);
var repository = uow.CreateRepository();
var query = memberTypeAlias == null ? null : repository.Query.Where(x => x.ContentTypeAlias == memberTypeAlias);
var members = repository.GetPagedResultsByQuery(query, pageIndex, pageSize, out totalRecords, orderBy, orderDirection, orderBySystemField, filter);
uow.Complete();
return members;
}
}
///
/// Gets an by its provider key
///
/// Id to use for retrieval
///
public IMember GetByProviderKey(object id)
{
var asGuid = id.TryConvertTo();
if (asGuid.Success)
return GetByKey(asGuid.Result);
var asInt = id.TryConvertTo();
if (asInt.Success)
return GetById(asInt.Result);
return null;
}
///
/// Get an by email
///
/// Email to use for retrieval
///
public IMember GetByEmail(string email)
{
using (var uow = UowProvider.CreateUnitOfWork())
{
uow.ReadLock(Constants.Locks.MemberTree);
var repository = uow.CreateRepository();
var query = repository.Query.Where(x => x.Email.Equals(email));
var member = repository.GetByQuery(query).FirstOrDefault();
uow.Complete();
return member;
}
}
///
/// Get an by username
///
/// Username to use for retrieval
///
public IMember GetByUsername(string username)
{
//TODO: Somewhere in here, whether at this level or the repository level, we need to add
// a caching mechanism since this method is used by all the membership providers and could be
// called quite a bit when dealing with members.
using (var uow = UowProvider.CreateUnitOfWork())
{
uow.ReadLock(Constants.Locks.MemberTree);
var repository = uow.CreateRepository();
var query = repository.Query.Where(x => x.Username.Equals(username));
var member = repository.GetByQuery(query).FirstOrDefault();
uow.Complete();
return member;
}
}
///
/// Gets all Members for the specified MemberType alias
///
/// Alias of the MemberType
///
public IEnumerable GetMembersByMemberType(string memberTypeAlias)
{
using (var uow = UowProvider.CreateUnitOfWork())
{
uow.ReadLock(Constants.Locks.MemberTree);
var repository = uow.CreateRepository();
var query = repository.Query.Where(x => x.ContentTypeAlias == memberTypeAlias);
var members = repository.GetByQuery(query);
uow.Complete();
return members;
}
}
///
/// Gets all Members for the MemberType id
///
/// Id of the MemberType
///
public IEnumerable GetMembersByMemberType(int memberTypeId)
{
using (var uow = UowProvider.CreateUnitOfWork())
{
uow.ReadLock(Constants.Locks.MemberTree);
var repository = uow.CreateRepository();
repository.Get(memberTypeId);
var query = repository.Query.Where(x => x.ContentTypeId == memberTypeId);
var members = repository.GetByQuery(query);
uow.Complete();
return members;
}
}
///
/// Gets all Members within the specified MemberGroup name
///
/// Name of the MemberGroup
///
public IEnumerable GetMembersByGroup(string memberGroupName)
{
using (var uow = UowProvider.CreateUnitOfWork())
{
uow.ReadLock(Constants.Locks.MemberTree);
var repository = uow.CreateRepository();
var members = repository.GetByMemberGroup(memberGroupName);
uow.Complete();
return members;
}
}
///
/// Gets all Members with the ids specified
///
/// If no Ids are specified all Members will be retrieved
/// Optional list of Member Ids
///
public IEnumerable GetAllMembers(params int[] ids)
{
using (var uow = UowProvider.CreateUnitOfWork())
{
uow.ReadLock(Constants.Locks.MemberTree);
var repository = uow.CreateRepository();
var members = repository.GetAll(ids);
uow.Complete();
return members;
}
}
///
/// Finds Members based on their display name
///
/// Display name to match
/// Current page index
/// Size of the page
/// Total number of records found (out)
/// The type of match to make as . Default is
///
public IEnumerable FindMembersByDisplayName(string displayNameToMatch, long pageIndex, int pageSize, out long totalRecords, StringPropertyMatchType matchType = StringPropertyMatchType.StartsWith)
{
using (var uow = UowProvider.CreateUnitOfWork())
{
uow.ReadLock(Constants.Locks.MemberTree);
var repository = uow.CreateRepository();
var query = repository.Query;
switch (matchType)
{
case StringPropertyMatchType.Exact:
query.Where(member => member.Name.Equals(displayNameToMatch));
break;
case StringPropertyMatchType.Contains:
query.Where(member => member.Name.Contains(displayNameToMatch));
break;
case StringPropertyMatchType.StartsWith:
query.Where(member => member.Name.StartsWith(displayNameToMatch));
break;
case StringPropertyMatchType.EndsWith:
query.Where(member => member.Name.EndsWith(displayNameToMatch));
break;
case StringPropertyMatchType.Wildcard:
query.Where(member => member.Name.SqlWildcard(displayNameToMatch, TextColumnType.NVarchar));
break;
default:
throw new ArgumentOutOfRangeException(nameof(matchType)); // causes rollback
}
var members = repository.GetPagedResultsByQuery(query, pageIndex, pageSize, out totalRecords, "Name", Direction.Ascending, true);
uow.Complete();
return members;
}
}
///
/// Finds a list of objects by a partial email string
///
/// Partial email string to match
/// Current page index
/// Size of the page
/// Total number of records found (out)
/// The type of match to make as . Default is
///
public IEnumerable FindByEmail(string emailStringToMatch, long pageIndex, int pageSize, out long totalRecords, StringPropertyMatchType matchType = StringPropertyMatchType.StartsWith)
{
using (var uow = UowProvider.CreateUnitOfWork())
{
uow.ReadLock(Constants.Locks.MemberTree);
var repository = uow.CreateRepository();
var query = repository.Query;
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));
}
var members = repository.GetPagedResultsByQuery(query, pageIndex, pageSize, out totalRecords, "Email", Direction.Ascending, true);
uow.Complete();
return members;
}
}
///
/// Finds a list of objects by a partial username
///
/// Partial username to match
/// Current page index
/// Size of the page
/// Total number of records found (out)
/// The type of match to make as . Default is
///
public IEnumerable FindByUsername(string login, long pageIndex, int pageSize, out long totalRecords, StringPropertyMatchType matchType = StringPropertyMatchType.StartsWith)
{
using (var uow = UowProvider.CreateUnitOfWork())
{
uow.ReadLock(Constants.Locks.MemberTree);
var repository = uow.CreateRepository();
var query = repository.Query;
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.Email.SqlWildcard(login, TextColumnType.NVarchar));
break;
default:
throw new ArgumentOutOfRangeException(nameof(matchType));
}
var members = repository.GetPagedResultsByQuery(query, pageIndex, pageSize, out totalRecords, "LoginName", Direction.Ascending, true);
uow.Complete();
return members;
}
}
///
/// Gets a list of Members based on a property search
///
/// Alias of the PropertyType to search for
/// Value to match
/// The type of match to make as . Default is
///
public IEnumerable GetMembersByPropertyValue(string propertyTypeAlias, string value, StringPropertyMatchType matchType = StringPropertyMatchType.Exact)
{
using (var uow = UowProvider.CreateUnitOfWork())
{
uow.ReadLock(Constants.Locks.MemberTree);
var repository = uow.CreateRepository();
IQuery query;
switch (matchType)
{
case StringPropertyMatchType.Exact:
query = repository.Query.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 = repository.Query.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 = repository.Query.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 = repository.Query.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)); // causes rollback
}
var members = repository.GetByQuery(query);
uow.Complete();
return members;
}
}
///
/// Gets a list of Members based on a property search
///
/// Alias of the PropertyType to search for
/// Value to match
/// The type of match to make as . Default is
///
public IEnumerable GetMembersByPropertyValue(string propertyTypeAlias, int value, ValuePropertyMatchType matchType = ValuePropertyMatchType.Exact)
{
using (var uow = UowProvider.CreateUnitOfWork())
{
uow.ReadLock(Constants.Locks.MemberTree);
var repository = uow.CreateRepository();
IQuery query;
switch (matchType)
{
case ValuePropertyMatchType.Exact:
query = repository.Query.Where(x =>
((Member)x).PropertyTypeAlias == propertyTypeAlias &&
((Member)x).IntegerPropertyValue == value);
break;
case ValuePropertyMatchType.GreaterThan:
query = repository.Query.Where(x =>
((Member)x).PropertyTypeAlias == propertyTypeAlias &&
((Member)x).IntegerPropertyValue > value);
break;
case ValuePropertyMatchType.LessThan:
query = repository.Query.Where(x =>
((Member)x).PropertyTypeAlias == propertyTypeAlias &&
((Member)x).IntegerPropertyValue < value);
break;
case ValuePropertyMatchType.GreaterThanOrEqualTo:
query = repository.Query.Where(x =>
((Member)x).PropertyTypeAlias == propertyTypeAlias &&
((Member)x).IntegerPropertyValue >= value);
break;
case ValuePropertyMatchType.LessThanOrEqualTo:
query = repository.Query.Where(x =>
((Member)x).PropertyTypeAlias == propertyTypeAlias &&
((Member)x).IntegerPropertyValue <= value);
break;
default:
throw new ArgumentOutOfRangeException(nameof(matchType)); // causes rollback
}
var members = repository.GetByQuery(query);
uow.Complete();
return members;
}
}
///
/// Gets a list of Members based on a property search
///
/// Alias of the PropertyType to search for
/// Value to match
///
public IEnumerable GetMembersByPropertyValue(string propertyTypeAlias, bool value)
{
using (var uow = UowProvider.CreateUnitOfWork())
{
uow.ReadLock(Constants.Locks.MemberTree);
var repository = uow.CreateRepository();
var query = repository.Query.Where(x =>
((Member)x).PropertyTypeAlias == propertyTypeAlias &&
((Member)x).BoolPropertyValue == value);
var members = repository.GetByQuery(query);
uow.Complete();
return members;
}
}
///
/// Gets a list of Members based on a property search
///
/// Alias of the PropertyType to search for
/// Value to match
/// The type of match to make as . Default is
///
public IEnumerable GetMembersByPropertyValue(string propertyTypeAlias, DateTime value, ValuePropertyMatchType matchType = ValuePropertyMatchType.Exact)
{
using (var uow = UowProvider.CreateUnitOfWork())
{
uow.ReadLock(Constants.Locks.MemberTree);
var repository = uow.CreateRepository();
IQuery query;
switch (matchType)
{
case ValuePropertyMatchType.Exact:
query = repository.Query.Where( x =>
((Member)x).PropertyTypeAlias == propertyTypeAlias &&
((Member)x).DateTimePropertyValue == value);
break;
case ValuePropertyMatchType.GreaterThan:
query = repository.Query.Where(x =>
((Member)x).PropertyTypeAlias == propertyTypeAlias &&
((Member)x).DateTimePropertyValue > value);
break;
case ValuePropertyMatchType.LessThan:
query = repository.Query.Where(x =>
((Member)x).PropertyTypeAlias == propertyTypeAlias &&
((Member)x).DateTimePropertyValue < value);
break;
case ValuePropertyMatchType.GreaterThanOrEqualTo:
query = repository.Query.Where(x =>
((Member)x).PropertyTypeAlias == propertyTypeAlias &&
((Member)x).DateTimePropertyValue >= value);
break;
case ValuePropertyMatchType.LessThanOrEqualTo:
query = repository.Query.Where(x =>
((Member)x).PropertyTypeAlias == propertyTypeAlias &&
((Member)x).DateTimePropertyValue <= value);
break;
default:
throw new ArgumentOutOfRangeException(nameof(matchType)); // causes rollback
}
//TODO: Since this is by property value, we need a GetByPropertyQuery on the repo!
var members = repository.GetByQuery(query);
uow.Complete();
return members;
}
}
///
/// Checks if a Member with the id exists
///
/// Id of the Member
/// True if the Member exists otherwise False
public bool Exists(int id)
{
using (var uow = UowProvider.CreateUnitOfWork())
{
uow.ReadLock(Constants.Locks.MemberTree);
var repository = uow.CreateRepository();
var exists = repository.Exists(id);
uow.Complete();
return exists;
}
}
///
/// Checks if a Member with the username exists
///
/// Username to check
/// True if the Member exists otherwise False
public bool Exists(string username)
{
using (var uow = UowProvider.CreateUnitOfWork())
{
uow.ReadLock(Constants.Locks.MemberTree);
var repository = uow.CreateRepository();
var exists = repository.Exists(username);
uow.Complete();
return exists;
}
}
#endregion
#region Save
///
/// Saves an
///
/// to Save
/// Optional parameter to raise events.
/// Default is True otherwise set to False to not raise events
public void Save(IMember member, bool raiseEvents = true)
{
if (raiseEvents && Saving.IsRaisedEventCancelled(new SaveEventArgs(member), this))
return;
using (var uow = UowProvider.CreateUnitOfWork())
{
uow.WriteLock(Constants.Locks.MemberTree);
var repository = uow.CreateRepository();
repository.AddOrUpdate(member);
// fixme get rid of xml
repository.AddOrUpdateContentXml(member, m => _entitySerializer.Serialize(_dataTypeService, m));
// generate preview for blame history?
if (UmbracoConfig.For.UmbracoSettings().Content.GlobalPreviewStorageEnabled)
repository.AddOrUpdatePreviewXml(member, m => _entitySerializer.Serialize(_dataTypeService, m));
uow.Complete();
}
if (raiseEvents)
Saved.RaiseEvent(new SaveEventArgs(member, false), this);
Audit(AuditType.Save, "Save Member performed by user", 0, member.Id);
}
///
/// Saves a list of objects
///
/// to save
/// Optional parameter to raise events.
/// Default is True otherwise set to False to not raise events
public void Save(IEnumerable members, bool raiseEvents = true)
{
var membersA = members.ToArray();
if (raiseEvents && Saving.IsRaisedEventCancelled(new SaveEventArgs(membersA), this))
return;
using (var uow = UowProvider.CreateUnitOfWork())
{
uow.WriteLock(Constants.Locks.MemberTree);
var repository = uow.CreateRepository();
foreach (var member in membersA)
{
repository.AddOrUpdate(member);
// fixme get rid of xml stuff
repository.AddOrUpdateContentXml(member, m => _entitySerializer.Serialize(_dataTypeService, m));
// generate preview for blame history?
if (UmbracoConfig.For.UmbracoSettings().Content.GlobalPreviewStorageEnabled)
repository.AddOrUpdatePreviewXml(member, m => _entitySerializer.Serialize(_dataTypeService, m));
}
uow.Complete();
}
if (raiseEvents)
Saved.RaiseEvent(new SaveEventArgs(membersA, false), this);
Audit(AuditType.Save, "Save Member items performed by user", 0, -1);
}
#endregion
#region Delete
///
/// Deletes an
///
/// to Delete
public void Delete(IMember member)
{
if (Deleting.IsRaisedEventCancelled(new DeleteEventArgs(member), this))
return;
using (var uow = UowProvider.CreateUnitOfWork())
{
uow.WriteLock(Constants.Locks.MemberTree);
var repository = uow.CreateRepository();
DeleteLocked(repository, member);
uow.Complete();
}
Audit(AuditType.Delete, "Delete Member performed by user", 0, member.Id);
}
private void DeleteLocked(IMemberRepository repository, IMember member)
{
// a member has no descendants
repository.Delete(member);
var args = new DeleteEventArgs(member, false); // raise event & get flagged files
Deleted.RaiseEvent(args, this);
IOHelper.DeleteFiles(args.MediaFilesToDelete, // remove flagged files
(file, e) => Logger.Error("An error occurred while deleting file attached to nodes: " + file, e));
}
#endregion
#region Roles
public void AddRole(string roleName)
{
using (var uow = UowProvider.CreateUnitOfWork())
{
uow.WriteLock(Constants.Locks.MemberTree);
var repository = uow.CreateRepository();
repository.CreateIfNotExists(roleName);
uow.Complete();
}
}
public IEnumerable GetAllRoles()
{
using (var uow = UowProvider.CreateUnitOfWork())
{
uow.ReadLock(Constants.Locks.MemberTree);
var repository = uow.CreateRepository();
var result = repository.GetAll().Select(x => x.Name).Distinct();
uow.Complete();
return result;
}
}
public IEnumerable GetAllRoles(int memberId)
{
using (var uow = UowProvider.CreateUnitOfWork())
{
uow.ReadLock(Constants.Locks.MemberTree);
var repository = uow.CreateRepository();
var result = repository.GetMemberGroupsForMember(memberId);
var roles = result.Select(x => x.Name).Distinct();
uow.Complete();
return roles;
}
}
public IEnumerable GetAllRoles(string username)
{
using (var uow = UowProvider.CreateUnitOfWork())
{
uow.ReadLock(Constants.Locks.MemberTree);
var repository = uow.CreateRepository();
var result = repository.GetMemberGroupsForMember(username);
var roles = result.Select(x => x.Name).Distinct();
uow.Complete();
return roles;
}
}
public IEnumerable GetMembersInRole(string roleName)
{
using (var uow = UowProvider.CreateUnitOfWork())
{
uow.ReadLock(Constants.Locks.MemberTree);
var repository = uow.CreateRepository();
var members = repository.GetByMemberGroup(roleName);
uow.Complete();
return members;
}
}
public IEnumerable FindMembersInRole(string roleName, string usernameToMatch, StringPropertyMatchType matchType = StringPropertyMatchType.StartsWith)
{
using (var uow = UowProvider.CreateUnitOfWork())
{
uow.ReadLock(Constants.Locks.MemberTree);
var repository = uow.CreateRepository();
var members = repository.FindMembersInRole(roleName, usernameToMatch, matchType);
uow.Complete();
return members;
}
}
// FIXME CURRENT WIP
public bool DeleteRole(string roleName, bool throwIfBeingUsed)
{
using (var uow = UowProvider.CreateUnitOfWork())
{
uow.WriteLock(Constants.Locks.MemberTree);
var repository = uow.CreateRepository();
if (throwIfBeingUsed)
{
// get members in role
var memberRepository = uow.CreateRepository();
var membersInRole = memberRepository.GetByMemberGroup(roleName);
if (membersInRole.Any())
throw new InvalidOperationException("The role " + roleName + " is currently assigned to members");
}
var query = repository.QueryFactory.Create().Where(g => g.Name == roleName);
var found = repository.GetByQuery(query).ToArray();
foreach (var memberGroup in found)
_memberGroupService.Delete(memberGroup); // FIXME BAD BAD BAD!
uow.Complete();
return found.Length > 0;
}
}
public void AssignRole(string username, string roleName)
{
AssignRoles(new[] { username }, new[] { roleName });
}
public void AssignRoles(string[] usernames, string[] roleNames)
{
using (var uow = UowProvider.CreateUnitOfWork())
{
uow.WriteLock(Constants.Locks.MemberTree);
var repository = uow.CreateRepository();
repository.AssignRoles(usernames, roleNames);
uow.Complete();
}
}
public void DissociateRole(string username, string roleName)
{
DissociateRoles(new[] { username }, new[] { roleName });
}
public void DissociateRoles(string[] usernames, string[] roleNames)
{
using (var uow = UowProvider.CreateUnitOfWork())
{
uow.WriteLock(Constants.Locks.MemberTree);
var repository = uow.CreateRepository();
repository.DissociateRoles(usernames, roleNames);
uow.Complete();
}
}
public void AssignRole(int memberId, string roleName)
{
AssignRoles(new[] { memberId }, new[] { roleName });
}
public void AssignRoles(int[] memberIds, string[] roleNames)
{
using (var uow = UowProvider.CreateUnitOfWork())
{
uow.WriteLock(Constants.Locks.MemberTree);
var repository = uow.CreateRepository();
repository.AssignRoles(memberIds, roleNames);
uow.Complete();
}
}
public void DissociateRole(int memberId, string roleName)
{
DissociateRoles(new[] { memberId }, new[] { roleName });
}
public void DissociateRoles(int[] memberIds, string[] roleNames)
{
using (var uow = UowProvider.CreateUnitOfWork())
{
uow.WriteLock(Constants.Locks.MemberTree);
var repository = uow.CreateRepository();
repository.DissociateRoles(memberIds, roleNames);
uow.Complete();
}
}
#endregion
#region Private Methods
private void Audit(AuditType type, string message, int userId, int objectId)
{
using (var uow = UowProvider.CreateUnitOfWork())
{
var repo = uow.CreateRepository();
repo.AddOrUpdate(new AuditItem(objectId, message, type, userId));
uow.Complete();
}
}
#endregion
#region Event Handlers
///
/// Occurs before Delete
///
public static event TypedEventHandler> Deleting;
///
/// Occurs after Delete
///
public static event TypedEventHandler> Deleted;
///
/// Occurs before Save
///
public static event TypedEventHandler> Saving;
///
/// Occurs after Create
///
///
/// Please note that the Member object has been created, but might not have been saved
/// so it does not have an identity yet (meaning no Id has been set).
///
public static event TypedEventHandler> Created;
///
/// Occurs after Save
///
public static event TypedEventHandler> Saved;
#endregion
#region Membership
///
/// This is simply a helper method which essentially just wraps the MembershipProvider's ChangePassword method
///
/// This method exists so that Umbraco developers can use one entry point to create/update
/// Members if they choose to.
/// The Member to save the password for
/// The password to encrypt and save
public void SavePassword(IMember member, string password)
{
if (member == null) throw new ArgumentNullException("member");
var provider = MembershipProviderExtensions.GetMembersMembershipProvider();
if (provider.IsUmbracoMembershipProvider())
provider.ChangePassword(member.Username, "", password);
else
throw new NotSupportedException("When using a non-Umbraco membership provider you must change the member password by using the MembershipProvider.ChangePassword method");
//go re-fetch the member and update the properties that may have changed
var result = GetByUsername(member.Username);
//should never be null but it could have been deleted by another thread.
// fixme - should LOCK! instead
if (result == null)
return;
member.RawPasswordValue = result.RawPasswordValue;
member.LastPasswordChangeDate = result.LastPasswordChangeDate;
member.UpdateDate = result.UpdateDate;
// fixme - not saving?
}
///
/// A helper method that will create a basic/generic member for use with a generic membership provider
///
///
internal static IMember CreateGenericMembershipProviderMember(string name, string email, string username, string password)
{
var identity = int.MaxValue;
var memType = new MemberType(-1);
var propGroup = new PropertyGroup
{
Name = "Membership",
Id = --identity
};
propGroup.PropertyTypes.Add(new PropertyType(Constants.PropertyEditors.TextboxAlias, DataTypeDatabaseType.Ntext, Constants.Conventions.Member.Comments)
{
Name = Constants.Conventions.Member.CommentsLabel,
SortOrder = 0,
Id = --identity,
Key = identity.ToGuid()
});
propGroup.PropertyTypes.Add(new PropertyType(Constants.PropertyEditors.TrueFalseAlias, DataTypeDatabaseType.Integer, Constants.Conventions.Member.IsApproved)
{
Name = Constants.Conventions.Member.IsApprovedLabel,
SortOrder = 3,
Id = --identity,
Key = identity.ToGuid()
});
propGroup.PropertyTypes.Add(new PropertyType(Constants.PropertyEditors.TrueFalseAlias, DataTypeDatabaseType.Integer, Constants.Conventions.Member.IsLockedOut)
{
Name = Constants.Conventions.Member.IsLockedOutLabel,
SortOrder = 4,
Id = --identity,
Key = identity.ToGuid()
});
propGroup.PropertyTypes.Add(new PropertyType(Constants.PropertyEditors.NoEditAlias, DataTypeDatabaseType.Date, Constants.Conventions.Member.LastLockoutDate)
{
Name = Constants.Conventions.Member.LastLockoutDateLabel,
SortOrder = 5,
Id = --identity,
Key = identity.ToGuid()
});
propGroup.PropertyTypes.Add(new PropertyType(Constants.PropertyEditors.NoEditAlias, DataTypeDatabaseType.Date, Constants.Conventions.Member.LastLoginDate)
{
Name = Constants.Conventions.Member.LastLoginDateLabel,
SortOrder = 6,
Id = --identity,
Key = identity.ToGuid()
});
propGroup.PropertyTypes.Add(new PropertyType(Constants.PropertyEditors.NoEditAlias, DataTypeDatabaseType.Date, Constants.Conventions.Member.LastPasswordChangeDate)
{
Name = Constants.Conventions.Member.LastPasswordChangeDateLabel,
SortOrder = 7,
Id = --identity,
Key = identity.ToGuid()
});
memType.PropertyGroups.Add(propGroup);
// should we "create member"?
var member = new Member(name, email, username, password, memType);
//we've assigned ids to the property types and groups but we also need to assign fake ids to the properties themselves.
foreach (var property in member.Properties)
{
property.Id = --identity;
}
return member;
}
#endregion
#region Content Types
///
/// Delete Members of the specified MemberType id
///
/// Id of the MemberType
public void DeleteMembersOfType(int memberTypeId)
{
// note: no tree to manage here
using (var uow = UowProvider.CreateUnitOfWork())
{
uow.WriteLock(Constants.Locks.MemberTree);
var repository = uow.CreateRepository();
//TODO: What about content that has the contenttype as part of its composition?
var query = repository.Query.Where(x => x.ContentTypeId == memberTypeId);
var members = repository.GetByQuery(query).ToArray();
if (Deleting.IsRaisedEventCancelled(new DeleteEventArgs(members), this))
return; // causes rollback
foreach (var member in members)
{
// delete media
// triggers the deleted event (and handles the files)
DeleteLocked(repository, member);
}
uow.Complete();
}
}
private IMemberType GetMemberType(string memberTypeAlias)
{
var memberType = MemberTypeService.Get(memberTypeAlias);
if (memberType == null)
throw new Exception(string.Format("No MemberType matching alias: \"{0}\".", memberTypeAlias));
return memberType;
}
[Obsolete("use MemberTypeService.GetDefault()")] // fixme kill!
public string GetDefaultMemberType()
{
return MemberTypeService.GetDefault();
}
#endregion
#region Xml - Should Move!
///
/// Rebuilds all xml content in the cmsContentXml table for all members
///
///
/// Only rebuild the xml structures for the content type ids passed in, if none then rebuilds the structures
/// for all members = USE WITH CARE!
///
/// True if publishing succeeded, otherwise False
public void RebuildXmlStructures(params int[] memberTypeIds)
{
using (var uow = UowProvider.CreateUnitOfWork())
{
var repository = uow.CreateRepository();
repository.RebuildXmlStructures(
member => _entitySerializer.Serialize(_dataTypeService, member),
contentTypeIds: memberTypeIds.Length == 0 ? null : memberTypeIds);
uow.Complete();
}
Audit(AuditType.Publish, "MemberService.RebuildXmlStructures completed, the xml has been regenerated in the database", 0, -1);
}
#endregion
}
}