2017-07-20 11:21:28 +02:00
|
|
|
|
using System;
|
2015-02-09 17:37:21 +11:00
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
|
using System.Collections.ObjectModel;
|
|
|
|
|
|
using System.Collections.Specialized;
|
2019-11-19 14:10:08 +11:00
|
|
|
|
using System.ComponentModel;
|
2017-08-14 18:21:48 +02:00
|
|
|
|
using System.Linq;
|
2020-01-07 13:50:38 +01:00
|
|
|
|
using Umbraco.Core;
|
2019-12-19 15:53:50 +01:00
|
|
|
|
using Umbraco.Core.Configuration;
|
2018-01-15 11:32:30 +01:00
|
|
|
|
using Umbraco.Core.Models.Entities;
|
2020-01-07 13:50:38 +01:00
|
|
|
|
using Umbraco.Core.Models.Identity;
|
2017-08-14 18:21:48 +02:00
|
|
|
|
using Umbraco.Core.Models.Membership;
|
2015-02-09 17:37:21 +11:00
|
|
|
|
|
2020-01-07 13:50:38 +01:00
|
|
|
|
namespace Umbraco.Web.Models.Identity
|
2015-02-09 17:37:21 +11:00
|
|
|
|
{
|
2017-08-14 18:21:48 +02:00
|
|
|
|
public class BackOfficeIdentityUser : IdentityUser<int, IIdentityUserLogin, IdentityUserRole<string>, IdentityUserClaim<int>>, IRememberBeingDirty
|
2015-02-09 17:37:21 +11:00
|
|
|
|
{
|
2018-01-12 10:48:36 +01:00
|
|
|
|
private string _email;
|
|
|
|
|
|
private string _userName;
|
|
|
|
|
|
private int _id;
|
|
|
|
|
|
private bool _hasIdentity;
|
|
|
|
|
|
private DateTime? _lastLoginDateUtc;
|
|
|
|
|
|
private bool _emailConfirmed;
|
|
|
|
|
|
private string _name;
|
|
|
|
|
|
private int _accessFailedCount;
|
|
|
|
|
|
private string _passwordHash;
|
|
|
|
|
|
private string _culture;
|
|
|
|
|
|
private ObservableCollection<IIdentityUserLogin> _logins;
|
|
|
|
|
|
private Lazy<IEnumerable<IIdentityUserLogin>> _getLogins;
|
|
|
|
|
|
private IReadOnlyUserGroup[] _groups;
|
|
|
|
|
|
private string[] _allowedSections;
|
|
|
|
|
|
private int[] _startMediaIds;
|
|
|
|
|
|
private int[] _startContentIds;
|
2018-07-18 13:19:14 +10:00
|
|
|
|
private DateTime? _lastPasswordChangeDateUtc;
|
|
|
|
|
|
|
2017-08-14 18:21:48 +02:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Used to construct a new instance without an identity
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="username"></param>
|
|
|
|
|
|
/// <param name="email">This is allowed to be null (but would need to be filled in if trying to persist this instance)</param>
|
|
|
|
|
|
/// <param name="culture"></param>
|
|
|
|
|
|
/// <returns></returns>
|
2019-12-19 15:53:50 +01:00
|
|
|
|
public static BackOfficeIdentityUser CreateNew(IGlobalSettings globalSettings, string username, string email, string culture)
|
2017-08-14 18:21:48 +02:00
|
|
|
|
{
|
2018-01-12 10:48:36 +01:00
|
|
|
|
if (string.IsNullOrWhiteSpace(username)) throw new ArgumentException("Value cannot be null or whitespace.", nameof(username));
|
|
|
|
|
|
if (string.IsNullOrWhiteSpace(culture)) throw new ArgumentException("Value cannot be null or whitespace.", nameof(culture));
|
2017-08-14 18:21:48 +02:00
|
|
|
|
|
2019-12-19 15:53:50 +01:00
|
|
|
|
var user = new BackOfficeIdentityUser(globalSettings, Array.Empty<IReadOnlyUserGroup>());
|
2017-08-14 18:21:48 +02:00
|
|
|
|
user.DisableChangeTracking();
|
|
|
|
|
|
user._userName = username;
|
|
|
|
|
|
user._email = email;
|
|
|
|
|
|
//we are setting minvalue here because the default is "0" which is the id of the admin user
|
|
|
|
|
|
//which we cannot allow because the admin user will always exist
|
|
|
|
|
|
user._id = int.MinValue;
|
|
|
|
|
|
user._hasIdentity = false;
|
|
|
|
|
|
user._culture = culture;
|
|
|
|
|
|
user.EnableChangeTracking();
|
|
|
|
|
|
return user;
|
|
|
|
|
|
}
|
2015-03-25 10:57:10 +11:00
|
|
|
|
|
2019-12-19 15:53:50 +01:00
|
|
|
|
private BackOfficeIdentityUser(IGlobalSettings globalSettings, IReadOnlyUserGroup[] groups)
|
2017-08-14 18:21:48 +02:00
|
|
|
|
{
|
2019-03-20 17:04:46 +01:00
|
|
|
|
_startMediaIds = Array.Empty<int>();
|
|
|
|
|
|
_startContentIds = Array.Empty<int>();
|
|
|
|
|
|
_allowedSections = Array.Empty<string>();
|
2019-12-19 15:53:50 +01:00
|
|
|
|
_culture = globalSettings.DefaultUILanguage;
|
2019-03-20 17:04:46 +01:00
|
|
|
|
|
|
|
|
|
|
// must initialize before setting groups
|
2018-03-21 09:06:32 +01:00
|
|
|
|
_roles = new ObservableCollection<IdentityUserRole<string>>();
|
|
|
|
|
|
_roles.CollectionChanged += _roles_CollectionChanged;
|
2019-03-20 17:04:46 +01:00
|
|
|
|
|
|
|
|
|
|
// use the property setters - they do more than just setting a field
|
|
|
|
|
|
Groups = groups;
|
2017-08-14 18:21:48 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Creates an existing user with the specified groups
|
|
|
|
|
|
/// </summary>
|
2019-12-19 15:53:50 +01:00
|
|
|
|
/// <param name="globalSettings"></param>
|
2017-08-14 18:21:48 +02:00
|
|
|
|
/// <param name="userId"></param>
|
|
|
|
|
|
/// <param name="groups"></param>
|
2019-12-19 15:53:50 +01:00
|
|
|
|
public BackOfficeIdentityUser(IGlobalSettings globalSettings, int userId, IEnumerable<IReadOnlyUserGroup> groups)
|
|
|
|
|
|
: this(globalSettings, groups.ToArray())
|
2015-06-26 16:59:40 +02:00
|
|
|
|
{
|
2019-03-20 17:04:46 +01:00
|
|
|
|
// use the property setters - they do more than just setting a field
|
|
|
|
|
|
Id = userId;
|
2015-06-26 16:59:40 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2017-08-14 18:21:48 +02:00
|
|
|
|
/// <summary>
|
2019-01-22 18:03:39 -05:00
|
|
|
|
/// Returns true if an Id has been set on this object this will be false if the object is new and not persisted to the database
|
2017-08-14 18:21:48 +02:00
|
|
|
|
/// </summary>
|
2017-11-07 19:49:14 +01:00
|
|
|
|
public bool HasIdentity => _hasIdentity;
|
2017-08-14 18:21:48 +02:00
|
|
|
|
|
|
|
|
|
|
public int[] CalculatedMediaStartNodeIds { get; internal set; }
|
|
|
|
|
|
public int[] CalculatedContentStartNodeIds { get; internal set; }
|
|
|
|
|
|
|
|
|
|
|
|
public override int Id
|
|
|
|
|
|
{
|
2017-11-07 19:49:14 +01:00
|
|
|
|
get => _id;
|
2017-08-14 18:21:48 +02:00
|
|
|
|
set
|
|
|
|
|
|
{
|
|
|
|
|
|
_id = value;
|
|
|
|
|
|
_hasIdentity = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Override Email so we can track changes to it
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public override string Email
|
|
|
|
|
|
{
|
2017-11-07 19:49:14 +01:00
|
|
|
|
get => _email;
|
2019-02-04 19:52:04 +11:00
|
|
|
|
set => _beingDirty.SetPropertyValueAndDetectChanges(value, ref _email, nameof(Email));
|
2017-08-14 18:21:48 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Override UserName so we can track changes to it
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public override string UserName
|
|
|
|
|
|
{
|
2017-11-07 19:49:14 +01:00
|
|
|
|
get => _userName;
|
2019-02-04 19:52:04 +11:00
|
|
|
|
set => _beingDirty.SetPropertyValueAndDetectChanges(value, ref _userName, nameof(UserName));
|
2017-08-14 18:21:48 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2018-02-22 14:06:26 +11:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// LastPasswordChangeDateUtc so we can track changes to it
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public override DateTime? LastPasswordChangeDateUtc
|
|
|
|
|
|
{
|
|
|
|
|
|
get { return _lastPasswordChangeDateUtc; }
|
2019-02-04 19:52:04 +11:00
|
|
|
|
set { _beingDirty.SetPropertyValueAndDetectChanges(value, ref _lastPasswordChangeDateUtc, nameof(LastPasswordChangeDateUtc)); }
|
2018-02-22 14:06:26 +11:00
|
|
|
|
}
|
|
|
|
|
|
|
2017-08-14 18:21:48 +02:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Override LastLoginDateUtc so we can track changes to it
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public override DateTime? LastLoginDateUtc
|
|
|
|
|
|
{
|
2017-11-07 19:49:14 +01:00
|
|
|
|
get => _lastLoginDateUtc;
|
2019-02-04 19:52:04 +11:00
|
|
|
|
set => _beingDirty.SetPropertyValueAndDetectChanges(value, ref _lastLoginDateUtc, nameof(LastLoginDateUtc));
|
2017-08-14 18:21:48 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Override EmailConfirmed so we can track changes to it
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public override bool EmailConfirmed
|
|
|
|
|
|
{
|
2017-11-07 19:49:14 +01:00
|
|
|
|
get => _emailConfirmed;
|
2019-02-04 19:52:04 +11:00
|
|
|
|
set => _beingDirty.SetPropertyValueAndDetectChanges(value, ref _emailConfirmed, nameof(EmailConfirmed));
|
2017-08-14 18:21:48 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2015-02-09 17:37:21 +11:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Gets/sets the user's real name
|
|
|
|
|
|
/// </summary>
|
2017-08-14 18:21:48 +02:00
|
|
|
|
public string Name
|
|
|
|
|
|
{
|
2017-11-07 19:49:14 +01:00
|
|
|
|
get => _name;
|
2019-02-04 19:52:04 +11:00
|
|
|
|
set => _beingDirty.SetPropertyValueAndDetectChanges(value, ref _name, nameof(Name));
|
2017-08-14 18:21:48 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Override AccessFailedCount so we can track changes to it
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public override int AccessFailedCount
|
|
|
|
|
|
{
|
2017-11-07 19:49:14 +01:00
|
|
|
|
get => _accessFailedCount;
|
2019-02-04 19:52:04 +11:00
|
|
|
|
set => _beingDirty.SetPropertyValueAndDetectChanges(value, ref _accessFailedCount, nameof(AccessFailedCount));
|
2017-08-14 18:21:48 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Override PasswordHash so we can track changes to it
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public override string PasswordHash
|
|
|
|
|
|
{
|
2017-11-07 19:49:14 +01:00
|
|
|
|
get => _passwordHash;
|
2019-02-04 19:52:04 +11:00
|
|
|
|
set => _beingDirty.SetPropertyValueAndDetectChanges(value, ref _passwordHash, nameof(PasswordHash));
|
2017-08-14 18:21:48 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Content start nodes assigned to the User (not ones assigned to the user's groups)
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public int[] StartContentIds
|
|
|
|
|
|
{
|
2017-11-07 19:49:14 +01:00
|
|
|
|
get => _startContentIds;
|
2017-08-14 18:21:48 +02:00
|
|
|
|
set
|
|
|
|
|
|
{
|
|
|
|
|
|
if (value == null) value = new int[0];
|
2019-02-04 19:52:04 +11:00
|
|
|
|
_beingDirty.SetPropertyValueAndDetectChanges(value, ref _startContentIds, nameof(StartContentIds), StartIdsComparer);
|
2017-08-14 18:21:48 +02:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Media start nodes assigned to the User (not ones assigned to the user's groups)
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public int[] StartMediaIds
|
|
|
|
|
|
{
|
2017-11-07 19:49:14 +01:00
|
|
|
|
get => _startMediaIds;
|
2017-08-14 18:21:48 +02:00
|
|
|
|
set
|
|
|
|
|
|
{
|
|
|
|
|
|
if (value == null) value = new int[0];
|
2019-02-04 19:52:04 +11:00
|
|
|
|
_beingDirty.SetPropertyValueAndDetectChanges(value, ref _startMediaIds, nameof(StartMediaIds), StartIdsComparer);
|
2017-08-14 18:21:48 +02:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// This is a readonly list of the user's allowed sections which are based on it's user groups
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public string[] AllowedSections
|
|
|
|
|
|
{
|
|
|
|
|
|
get { return _allowedSections ?? (_allowedSections = _groups.SelectMany(x => x.AllowedSections).Distinct().ToArray()); }
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public string Culture
|
|
|
|
|
|
{
|
2017-11-07 19:49:14 +01:00
|
|
|
|
get => _culture;
|
2019-02-04 19:52:04 +11:00
|
|
|
|
set => _beingDirty.SetPropertyValueAndDetectChanges(value, ref _culture, nameof(Culture));
|
2017-08-14 18:21:48 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public IReadOnlyUserGroup[] Groups
|
|
|
|
|
|
{
|
2017-11-07 19:49:14 +01:00
|
|
|
|
get => _groups;
|
2017-08-14 18:21:48 +02:00
|
|
|
|
set
|
|
|
|
|
|
{
|
|
|
|
|
|
//so they recalculate
|
|
|
|
|
|
_allowedSections = null;
|
2015-02-09 17:37:21 +11:00
|
|
|
|
|
2019-03-20 17:04:46 +01:00
|
|
|
|
_groups = value;
|
|
|
|
|
|
|
2017-08-14 18:21:48 +02:00
|
|
|
|
//now clear all roles and re-add them
|
|
|
|
|
|
_roles.CollectionChanged -= _roles_CollectionChanged;
|
|
|
|
|
|
_roles.Clear();
|
|
|
|
|
|
foreach (var identityUserRole in _groups.Select(x => new IdentityUserRole<string>
|
|
|
|
|
|
{
|
|
|
|
|
|
RoleId = x.Alias,
|
|
|
|
|
|
UserId = Id.ToString()
|
|
|
|
|
|
}))
|
|
|
|
|
|
{
|
|
|
|
|
|
_roles.Add(identityUserRole);
|
|
|
|
|
|
}
|
|
|
|
|
|
_roles.CollectionChanged += _roles_CollectionChanged;
|
|
|
|
|
|
|
2019-02-04 19:52:04 +11:00
|
|
|
|
_beingDirty.SetPropertyValueAndDetectChanges(value, ref _groups, nameof(Groups), GroupsComparer);
|
2017-08-14 18:21:48 +02:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2015-02-19 16:06:07 +01:00
|
|
|
|
|
2015-07-01 18:02:58 +02:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Lockout is always enabled
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public override bool LockoutEnabled
|
|
|
|
|
|
{
|
|
|
|
|
|
get { return true; }
|
2017-07-20 11:21:28 +02:00
|
|
|
|
set
|
2015-07-01 18:02:58 +02:00
|
|
|
|
{
|
2017-07-20 11:21:28 +02:00
|
|
|
|
//do nothing
|
2015-07-01 18:02:58 +02:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Based on the user's lockout end date, this will determine if they are locked out
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
internal bool IsLockedOut
|
|
|
|
|
|
{
|
|
|
|
|
|
get
|
|
|
|
|
|
{
|
2017-11-07 19:49:14 +01:00
|
|
|
|
var isLocked = LockoutEndDateUtc.HasValue && LockoutEndDateUtc.Value.ToLocalTime() >= DateTime.Now;
|
2015-07-01 18:02:58 +02:00
|
|
|
|
return isLocked;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2017-09-15 18:22:19 +02:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// This is a 1:1 mapping with IUser.IsApproved
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
internal bool IsApproved { get; set; }
|
|
|
|
|
|
|
2015-02-09 17:37:21 +11:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Overridden to make the retrieval lazy
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public override ICollection<IIdentityUserLogin> Logins
|
|
|
|
|
|
{
|
|
|
|
|
|
get
|
|
|
|
|
|
{
|
|
|
|
|
|
if (_getLogins != null && _getLogins.IsValueCreated == false)
|
|
|
|
|
|
{
|
|
|
|
|
|
_logins = new ObservableCollection<IIdentityUserLogin>();
|
|
|
|
|
|
foreach (var l in _getLogins.Value)
|
|
|
|
|
|
{
|
|
|
|
|
|
_logins.Add(l);
|
|
|
|
|
|
}
|
|
|
|
|
|
//now assign events
|
|
|
|
|
|
_logins.CollectionChanged += Logins_CollectionChanged;
|
|
|
|
|
|
}
|
|
|
|
|
|
return _logins;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void Logins_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
|
|
|
|
|
|
{
|
2019-02-04 19:52:04 +11:00
|
|
|
|
_beingDirty.OnPropertyChanged(nameof(Logins));
|
2015-02-09 17:37:21 +11:00
|
|
|
|
}
|
|
|
|
|
|
|
2017-08-14 18:21:48 +02:00
|
|
|
|
private void _roles_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
|
|
|
|
|
|
{
|
2019-02-04 19:52:04 +11:00
|
|
|
|
_beingDirty.OnPropertyChanged(nameof(Roles));
|
2017-08-14 18:21:48 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private readonly ObservableCollection<IdentityUserRole<string>> _roles;
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// helper method to easily add a role without having to deal with IdentityUserRole{T}
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="role"></param>
|
|
|
|
|
|
/// <remarks>
|
|
|
|
|
|
/// Adding a role this way will not reflect on the user's group's collection or it's allowed sections until the user is persisted
|
|
|
|
|
|
/// </remarks>
|
|
|
|
|
|
public void AddRole(string role)
|
|
|
|
|
|
{
|
|
|
|
|
|
Roles.Add(new IdentityUserRole<string>
|
|
|
|
|
|
{
|
2017-11-07 19:49:14 +01:00
|
|
|
|
UserId = Id.ToString(),
|
2017-08-14 18:21:48 +02:00
|
|
|
|
RoleId = role
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Override Roles because the value of these are the user's group aliases
|
|
|
|
|
|
/// </summary>
|
2017-11-07 19:49:14 +01:00
|
|
|
|
public override ICollection<IdentityUserRole<string>> Roles => _roles;
|
2015-02-09 17:37:21 +11:00
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Used to set a lazy call back to populate the user's Login list
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="callback"></param>
|
|
|
|
|
|
public void SetLoginsCallback(Lazy<IEnumerable<IIdentityUserLogin>> callback)
|
|
|
|
|
|
{
|
2018-01-12 10:48:36 +01:00
|
|
|
|
_getLogins = callback ?? throw new ArgumentNullException(nameof(callback));
|
2015-02-09 17:37:21 +11:00
|
|
|
|
}
|
2017-08-14 18:21:48 +02:00
|
|
|
|
|
2018-01-12 10:48:36 +01:00
|
|
|
|
#region BeingDirty
|
2017-08-14 18:21:48 +02:00
|
|
|
|
|
2018-01-12 10:48:36 +01:00
|
|
|
|
private readonly BeingDirty _beingDirty = new BeingDirty();
|
|
|
|
|
|
|
|
|
|
|
|
/// <inheritdoc />
|
|
|
|
|
|
public bool IsDirty()
|
2017-08-14 18:21:48 +02:00
|
|
|
|
{
|
2018-01-12 10:48:36 +01:00
|
|
|
|
return _beingDirty.IsDirty();
|
2017-08-14 18:21:48 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2018-01-12 10:48:36 +01:00
|
|
|
|
/// <inheritdoc />
|
|
|
|
|
|
public bool IsPropertyDirty(string propName)
|
2017-08-14 18:21:48 +02:00
|
|
|
|
{
|
2018-01-12 10:48:36 +01:00
|
|
|
|
return _beingDirty.IsPropertyDirty(propName);
|
2017-08-14 18:21:48 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2018-01-12 10:48:36 +01:00
|
|
|
|
/// <inheritdoc />
|
|
|
|
|
|
public IEnumerable<string> GetDirtyProperties()
|
2017-08-14 18:21:48 +02:00
|
|
|
|
{
|
2018-01-12 10:48:36 +01:00
|
|
|
|
return _beingDirty.GetDirtyProperties();
|
2017-08-14 18:21:48 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2018-01-12 10:48:36 +01:00
|
|
|
|
/// <inheritdoc />
|
|
|
|
|
|
public void ResetDirtyProperties()
|
2017-08-14 18:21:48 +02:00
|
|
|
|
{
|
2018-01-12 10:48:36 +01:00
|
|
|
|
_beingDirty.ResetDirtyProperties();
|
2017-08-14 18:21:48 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2018-01-12 10:48:36 +01:00
|
|
|
|
/// <inheritdoc />
|
|
|
|
|
|
public bool WasDirty()
|
2017-08-14 18:21:48 +02:00
|
|
|
|
{
|
2018-01-12 10:48:36 +01:00
|
|
|
|
return _beingDirty.WasDirty();
|
2017-08-14 18:21:48 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2018-01-12 10:48:36 +01:00
|
|
|
|
/// <inheritdoc />
|
|
|
|
|
|
public bool WasPropertyDirty(string propertyName)
|
2017-08-14 18:21:48 +02:00
|
|
|
|
{
|
2018-01-12 10:48:36 +01:00
|
|
|
|
return _beingDirty.WasPropertyDirty(propertyName);
|
2017-08-14 18:21:48 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2018-01-12 10:48:36 +01:00
|
|
|
|
/// <inheritdoc />
|
|
|
|
|
|
public void ResetWereDirtyProperties()
|
2017-08-14 18:21:48 +02:00
|
|
|
|
{
|
2018-01-12 10:48:36 +01:00
|
|
|
|
_beingDirty.ResetWereDirtyProperties();
|
2017-08-14 18:21:48 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2018-01-12 10:48:36 +01:00
|
|
|
|
/// <inheritdoc />
|
|
|
|
|
|
public void ResetDirtyProperties(bool rememberDirty)
|
2017-08-14 18:21:48 +02:00
|
|
|
|
{
|
2018-01-12 10:48:36 +01:00
|
|
|
|
_beingDirty.ResetDirtyProperties(rememberDirty);
|
2017-08-14 18:21:48 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2018-03-22 17:41:13 +01:00
|
|
|
|
/// <inheritdoc />
|
|
|
|
|
|
public IEnumerable<string> GetWereDirtyProperties()
|
|
|
|
|
|
=> _beingDirty.GetWereDirtyProperties();
|
|
|
|
|
|
|
2018-01-12 10:48:36 +01:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Disables change tracking.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public void DisableChangeTracking()
|
2017-08-14 18:21:48 +02:00
|
|
|
|
{
|
2018-01-12 10:48:36 +01:00
|
|
|
|
_beingDirty.DisableChangeTracking();
|
2017-11-07 19:49:14 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2018-01-12 10:48:36 +01:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Enables change tracking.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public void EnableChangeTracking()
|
2017-11-07 19:49:14 +01:00
|
|
|
|
{
|
2018-01-12 10:48:36 +01:00
|
|
|
|
_beingDirty.EnableChangeTracking();
|
2017-08-14 18:21:48 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2019-11-19 14:10:08 +11:00
|
|
|
|
public event PropertyChangedEventHandler PropertyChanged
|
|
|
|
|
|
{
|
|
|
|
|
|
add
|
|
|
|
|
|
{
|
|
|
|
|
|
_beingDirty.PropertyChanged += value;
|
|
|
|
|
|
}
|
|
|
|
|
|
remove
|
|
|
|
|
|
{
|
|
|
|
|
|
_beingDirty.PropertyChanged -= value;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2018-01-12 10:48:36 +01:00
|
|
|
|
#endregion
|
|
|
|
|
|
|
2019-02-04 19:52:04 +11:00
|
|
|
|
//Custom comparer for enumerables
|
|
|
|
|
|
private static readonly DelegateEqualityComparer<IReadOnlyUserGroup[]> GroupsComparer = new DelegateEqualityComparer<IReadOnlyUserGroup[]>(
|
|
|
|
|
|
(groups, enumerable) => groups.Select(x => x.Alias).UnsortedSequenceEqual(enumerable.Select(x => x.Alias)),
|
|
|
|
|
|
groups => groups.GetHashCode());
|
2019-02-04 10:09:32 +01:00
|
|
|
|
|
2019-02-04 19:52:04 +11:00
|
|
|
|
private static readonly DelegateEqualityComparer<int[]> StartIdsComparer = new DelegateEqualityComparer<int[]>(
|
|
|
|
|
|
(groups, enumerable) => groups.UnsortedSequenceEqual(enumerable),
|
|
|
|
|
|
groups => groups.GetHashCode());
|
2019-12-19 15:53:50 +01:00
|
|
|
|
|
2015-02-09 17:37:21 +11:00
|
|
|
|
}
|
2017-07-20 11:21:28 +02:00
|
|
|
|
}
|