Merge remote-tracking branch 'origin/netcore/dev' into netcore/task/member-macros-10577
This commit is contained in:
@@ -16,16 +16,6 @@ namespace Umbraco.Cms.Core.Composing
|
||||
{
|
||||
private readonly Assembly _entryPointAssembly;
|
||||
private readonly ILoggerFactory _loggerFactory;
|
||||
private static readonly string[] UmbracoCoreAssemblyNames = new[]
|
||||
{
|
||||
"Umbraco.Core",
|
||||
"Umbraco.Infrastructure",
|
||||
"Umbraco.PublishedCache.NuCache",
|
||||
"Umbraco.Examine.Lucene",
|
||||
"Umbraco.Web.Common",
|
||||
"Umbraco.Web.BackOffice",
|
||||
"Umbraco.Web.Website",
|
||||
};
|
||||
|
||||
public DefaultUmbracoAssemblyProvider(Assembly entryPointAssembly, ILoggerFactory loggerFactory)
|
||||
{
|
||||
@@ -43,7 +33,7 @@ namespace Umbraco.Cms.Core.Composing
|
||||
{
|
||||
get
|
||||
{
|
||||
var finder = new FindAssembliesWithReferencesTo(new[] { _entryPointAssembly }, UmbracoCoreAssemblyNames, true, _loggerFactory);
|
||||
var finder = new FindAssembliesWithReferencesTo(new[] { _entryPointAssembly }, Constants.Composing.UmbracoCoreAssemblyNames, true, _loggerFactory);
|
||||
return finder.Find();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,9 +82,8 @@ namespace Umbraco.Cms.Core.Composing
|
||||
assemblyName.FullName.StartsWith(f, StringComparison.InvariantCultureIgnoreCase)))
|
||||
continue;
|
||||
|
||||
// don't include this item if it's Umbraco
|
||||
// TODO: We should maybe pass an explicit list of these names in?
|
||||
if (assemblyName.FullName.StartsWith("Umbraco.") || assemblyName.Name.EndsWith(".Views"))
|
||||
// don't include this item if it's Umbraco Core
|
||||
if (Constants.Composing.UmbracoCoreAssemblyNames.Any(x=>assemblyName.FullName.StartsWith(x) || assemblyName.Name.EndsWith(".Views")))
|
||||
continue;
|
||||
|
||||
var assembly = Assembly.Load(assemblyName);
|
||||
|
||||
@@ -9,6 +9,17 @@
|
||||
/// Defines constants for composition.
|
||||
/// </summary>
|
||||
public static class Composing
|
||||
{ }
|
||||
{
|
||||
public static readonly string[] UmbracoCoreAssemblyNames = new[]
|
||||
{
|
||||
"Umbraco.Core",
|
||||
"Umbraco.Infrastructure",
|
||||
"Umbraco.PublishedCache.NuCache",
|
||||
"Umbraco.Examine.Lucene",
|
||||
"Umbraco.Web.Common",
|
||||
"Umbraco.Web.BackOffice",
|
||||
"Umbraco.Web.Website",
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Umbraco.Cms.Core.Composing;
|
||||
using System;
|
||||
using Umbraco.Cms.Core.Composing;
|
||||
|
||||
namespace Umbraco.Cms.Core.Dashboards
|
||||
{
|
||||
@@ -7,21 +8,10 @@ namespace Umbraco.Cms.Core.Dashboards
|
||||
{
|
||||
public string Alias => "contentIntro";
|
||||
|
||||
public string[] Sections => new [] { "content" };
|
||||
public string[] Sections => new[] { "content" };
|
||||
|
||||
public string View => "views/dashboard/default/startupdashboardintro.html";
|
||||
|
||||
public IAccessRule[] AccessRules
|
||||
{
|
||||
get
|
||||
{
|
||||
var rules = new IAccessRule[]
|
||||
{
|
||||
new AccessRule {Type = AccessRuleType.Deny, Value = Constants.Security.TranslatorGroupAlias},
|
||||
new AccessRule {Type = AccessRuleType.Grant, Value = Constants.Security.AdminGroupAlias}
|
||||
};
|
||||
return rules;
|
||||
}
|
||||
}
|
||||
public IAccessRule[] AccessRules { get; } = Array.Empty<IAccessRule>();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,7 +30,8 @@ namespace Umbraco.Cms.Core.DependencyInjection
|
||||
builder.Services.AddSingleton<IValidateOptions<UnattendedSettings>, UnattendedSettingsValidator>();
|
||||
|
||||
// Register configuration sections.
|
||||
builder.Services.Configure<ConnectionStrings>(builder.Config.GetSection(Constants.Configuration.ConfigModelsBuilder), o => o.BindNonPublicProperties = true);
|
||||
|
||||
builder.Services.Configure<ModelsBuilderSettings>(builder.Config.GetSection(Constants.Configuration.ConfigModelsBuilder), o => o.BindNonPublicProperties = true);
|
||||
builder.Services.Configure<ConnectionStrings>(builder.Config.GetSection("ConnectionStrings"), o => o.BindNonPublicProperties = true);
|
||||
|
||||
AddOptions<ActiveDirectorySettings>(builder, Constants.Configuration.ConfigActiveDirectory);
|
||||
|
||||
11
src/Umbraco.Core/Events/DataTypeDeletedNotification.cs
Normal file
11
src/Umbraco.Core/Events/DataTypeDeletedNotification.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
using Umbraco.Cms.Core.Models;
|
||||
|
||||
namespace Umbraco.Cms.Core.Events
|
||||
{
|
||||
public class DataTypeDeletedNotification : DeletedNotification<IDataType>
|
||||
{
|
||||
public DataTypeDeletedNotification(IDataType target, EventMessages messages) : base(target, messages)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
11
src/Umbraco.Core/Events/DataTypeDeletingNotification.cs
Normal file
11
src/Umbraco.Core/Events/DataTypeDeletingNotification.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
using Umbraco.Cms.Core.Models;
|
||||
|
||||
namespace Umbraco.Cms.Core.Events
|
||||
{
|
||||
public class DataTypeDeletingNotification : DeletingNotification<IDataType>
|
||||
{
|
||||
public DataTypeDeletingNotification(IDataType target, EventMessages messages) : base(target, messages)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
11
src/Umbraco.Core/Events/DataTypeMovedNotification.cs
Normal file
11
src/Umbraco.Core/Events/DataTypeMovedNotification.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
using Umbraco.Cms.Core.Models;
|
||||
|
||||
namespace Umbraco.Cms.Core.Events
|
||||
{
|
||||
public class DataTypeMovedNotification : MovedNotification<IDataType>
|
||||
{
|
||||
public DataTypeMovedNotification(MoveEventInfo<IDataType> target, EventMessages messages) : base(target, messages)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
11
src/Umbraco.Core/Events/DataTypeMovingNotification.cs
Normal file
11
src/Umbraco.Core/Events/DataTypeMovingNotification.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
using Umbraco.Cms.Core.Models;
|
||||
|
||||
namespace Umbraco.Cms.Core.Events
|
||||
{
|
||||
public class DataTypeMovingNotification : MovingNotification<IDataType>
|
||||
{
|
||||
public DataTypeMovingNotification(MoveEventInfo<IDataType> target, EventMessages messages) : base(target, messages)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
16
src/Umbraco.Core/Events/DataTypeSavedNotification.cs
Normal file
16
src/Umbraco.Core/Events/DataTypeSavedNotification.cs
Normal file
@@ -0,0 +1,16 @@
|
||||
using System.Collections.Generic;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
|
||||
namespace Umbraco.Cms.Core.Events
|
||||
{
|
||||
public class DataTypeSavedNotification : SavedNotification<IDataType>
|
||||
{
|
||||
public DataTypeSavedNotification(IDataType target, EventMessages messages) : base(target, messages)
|
||||
{
|
||||
}
|
||||
|
||||
public DataTypeSavedNotification(IEnumerable<IDataType> target, EventMessages messages) : base(target, messages)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
16
src/Umbraco.Core/Events/DataTypeSavingNotification.cs
Normal file
16
src/Umbraco.Core/Events/DataTypeSavingNotification.cs
Normal file
@@ -0,0 +1,16 @@
|
||||
using System.Collections.Generic;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
|
||||
namespace Umbraco.Cms.Core.Events
|
||||
{
|
||||
public class DataTypeSavingNotification : SavingNotification<IDataType>
|
||||
{
|
||||
public DataTypeSavingNotification(IDataType target, EventMessages messages) : base(target, messages)
|
||||
{
|
||||
}
|
||||
|
||||
public DataTypeSavingNotification(IEnumerable<IDataType> target, EventMessages messages) : base(target, messages)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,8 @@
|
||||
// Copyright (c) Umbraco.
|
||||
// See LICENSE for more details.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using Umbraco.Cms.Core.Events;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Services.Notifications
|
||||
namespace Umbraco.Cms.Core.Events
|
||||
{
|
||||
public abstract class DeletingNotification<T> : CancelableEnumerableObjectNotification<T>
|
||||
{
|
||||
@@ -0,0 +1,11 @@
|
||||
using Umbraco.Cms.Core.Models;
|
||||
|
||||
namespace Umbraco.Cms.Core.Events
|
||||
{
|
||||
public class EntityContainerDeletedNotification : DeletedNotification<EntityContainer>
|
||||
{
|
||||
public EntityContainerDeletedNotification(EntityContainer target, EventMessages messages) : base(target, messages)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
using Umbraco.Cms.Core.Models;
|
||||
|
||||
namespace Umbraco.Cms.Core.Events
|
||||
{
|
||||
public class EntityContainerDeletingNotification : DeletingNotification<EntityContainer>
|
||||
{
|
||||
public EntityContainerDeletingNotification(EntityContainer target, EventMessages messages) : base(target, messages)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
using Umbraco.Cms.Core.Models;
|
||||
|
||||
namespace Umbraco.Cms.Core.Events
|
||||
{
|
||||
public class EntityContainerRenamedNotification : RenamedNotification<EntityContainer>
|
||||
{
|
||||
public EntityContainerRenamedNotification(EntityContainer target, EventMessages messages) : base(target, messages)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
using Umbraco.Cms.Core.Models;
|
||||
|
||||
namespace Umbraco.Cms.Core.Events
|
||||
{
|
||||
public class EntityContainerRenamingNotification : RenamingNotification<EntityContainer>
|
||||
{
|
||||
public EntityContainerRenamingNotification(EntityContainer target, EventMessages messages) : base(target, messages)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
11
src/Umbraco.Core/Events/EntityContainerSavedNotification.cs
Normal file
11
src/Umbraco.Core/Events/EntityContainerSavedNotification.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
using Umbraco.Cms.Core.Models;
|
||||
|
||||
namespace Umbraco.Cms.Core.Events
|
||||
{
|
||||
public class EntityContainerSavedNotification : SavedNotification<EntityContainer>
|
||||
{
|
||||
public EntityContainerSavedNotification(EntityContainer target, EventMessages messages) : base(target, messages)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
11
src/Umbraco.Core/Events/EntityContainerSavingNotification.cs
Normal file
11
src/Umbraco.Core/Events/EntityContainerSavingNotification.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
using Umbraco.Cms.Core.Models;
|
||||
|
||||
namespace Umbraco.Cms.Core.Events
|
||||
{
|
||||
public class EntityContainerSavingNotification : SavingNotification<EntityContainer>
|
||||
{
|
||||
public EntityContainerSavingNotification(EntityContainer target, EventMessages messages) : base(target, messages)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
20
src/Umbraco.Core/Events/RenamedNotification.cs
Normal file
20
src/Umbraco.Core/Events/RenamedNotification.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
// Copyright (c) Umbraco.
|
||||
// See LICENSE for more details.
|
||||
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Umbraco.Cms.Core.Events
|
||||
{
|
||||
public abstract class RenamedNotification<T> : EnumerableObjectNotification<T>
|
||||
{
|
||||
protected RenamedNotification(T target, EventMessages messages) : base(target, messages)
|
||||
{
|
||||
}
|
||||
|
||||
protected RenamedNotification(IEnumerable<T> target, EventMessages messages) : base(target, messages)
|
||||
{
|
||||
}
|
||||
|
||||
public IEnumerable<T> Entities => Target;
|
||||
}
|
||||
}
|
||||
20
src/Umbraco.Core/Events/RenamingNotification.cs
Normal file
20
src/Umbraco.Core/Events/RenamingNotification.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
// Copyright (c) Umbraco.
|
||||
// See LICENSE for more details.
|
||||
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Umbraco.Cms.Core.Events
|
||||
{
|
||||
public abstract class RenamingNotification<T> : CancelableEnumerableObjectNotification<T>
|
||||
{
|
||||
protected RenamingNotification(T target, EventMessages messages) : base(target, messages)
|
||||
{
|
||||
}
|
||||
|
||||
protected RenamingNotification(IEnumerable<T> target, EventMessages messages) : base(target, messages)
|
||||
{
|
||||
}
|
||||
|
||||
public IEnumerable<T> Entities => Target;
|
||||
}
|
||||
}
|
||||
@@ -119,7 +119,7 @@ namespace Umbraco.Cms.Core.Models.Mapping
|
||||
Value = _localizedTextService.UmbracoDictionaryTranslate(CultureDictionary, member.ContentType.Name),
|
||||
View = _propertyEditorCollection[Constants.PropertyEditors.Aliases.Label].GetValueEditor().View
|
||||
},
|
||||
GetLoginProperty(_memberTypeService, member, _localizedTextService),
|
||||
GetLoginProperty(member, _localizedTextService),
|
||||
new ContentPropertyDisplay
|
||||
{
|
||||
Alias = $"{Constants.PropertyEditors.InternalGenericPropertiesPrefix}email",
|
||||
@@ -208,7 +208,6 @@ namespace Umbraco.Cms.Core.Models.Mapping
|
||||
/// <summary>
|
||||
/// Returns the login property display field
|
||||
/// </summary>
|
||||
/// <param name="memberTypeService"></param>
|
||||
/// <param name="member"></param>
|
||||
/// <param name="display"></param>
|
||||
/// <param name="localizedText"></param>
|
||||
@@ -218,7 +217,7 @@ namespace Umbraco.Cms.Core.Models.Mapping
|
||||
/// the membership provider is a custom one, we cannot allow changing the username because MembershipProvider's do not actually natively
|
||||
/// allow that.
|
||||
/// </remarks>
|
||||
internal static ContentPropertyDisplay GetLoginProperty(IMemberTypeService memberTypeService, IMember member, ILocalizedTextService localizedText)
|
||||
internal static ContentPropertyDisplay GetLoginProperty(IMember member, ILocalizedTextService localizedText)
|
||||
{
|
||||
var prop = new ContentPropertyDisplay
|
||||
{
|
||||
@@ -234,7 +233,7 @@ namespace Umbraco.Cms.Core.Models.Mapping
|
||||
|
||||
internal IDictionary<string, bool> GetMemberGroupValue(string username)
|
||||
{
|
||||
var userRoles = username.IsNullOrWhiteSpace() ? null : _memberService.GetAllRoles(username);
|
||||
IEnumerable<string> userRoles = username.IsNullOrWhiteSpace() ? null : _memberService.GetAllRoles(username);
|
||||
|
||||
// create a dictionary of all roles (except internal roles) + "false"
|
||||
var result = _memberGroupService.GetAll()
|
||||
@@ -245,11 +244,16 @@ namespace Umbraco.Cms.Core.Models.Mapping
|
||||
.ToDictionary(x => x, x => false);
|
||||
|
||||
// if user has no roles, just return the dictionary
|
||||
if (userRoles == null) return result;
|
||||
if (userRoles == null)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
// else update the dictionary to "true" for the user roles (except internal roles)
|
||||
foreach (var userRole in userRoles.Where(x => x.StartsWith(Constants.Conventions.Member.InternalRolePrefix) == false))
|
||||
{
|
||||
result[userRole] = true;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
@@ -46,6 +46,17 @@
|
||||
<AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleTo">
|
||||
<_Parameter1>DynamicProxyGenAssembly2</_Parameter1>
|
||||
</AssemblyAttribute>
|
||||
|
||||
<!-- Making internals visible to Umbraco Forms -->
|
||||
<AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleTo">
|
||||
<_Parameter1>Umbraco.Forms.Core</_Parameter1>
|
||||
</AssemblyAttribute>
|
||||
<AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleTo">
|
||||
<_Parameter1>Umbraco.Forms.Core.Providers</_Parameter1>
|
||||
</AssemblyAttribute>
|
||||
<AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleTo">
|
||||
<_Parameter1>Umbraco.Forms.Web</_Parameter1>
|
||||
</AssemblyAttribute>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -11,6 +11,7 @@ using Umbraco.Cms.Core.Models.Membership;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.Core.Services.Changes;
|
||||
using Umbraco.Cms.Core.Services.Implement;
|
||||
using Umbraco.Cms.Infrastructure.Services.Notifications;
|
||||
using Umbraco.Extensions;
|
||||
|
||||
namespace Umbraco.Cms.Core.Cache
|
||||
@@ -18,7 +19,23 @@ namespace Umbraco.Cms.Core.Cache
|
||||
/// <summary>
|
||||
/// Default <see cref="IDistributedCacheBinder"/> implementation.
|
||||
/// </summary>
|
||||
public partial class DistributedCacheBinder
|
||||
public partial class DistributedCacheBinder :
|
||||
INotificationHandler<DictionaryItemDeletedNotification>,
|
||||
INotificationHandler<DictionaryItemSavedNotification>,
|
||||
INotificationHandler<LanguageSavedNotification>,
|
||||
INotificationHandler<LanguageDeletedNotification>,
|
||||
INotificationHandler<MemberSavedNotification>,
|
||||
INotificationHandler<MemberDeletedNotification>,
|
||||
INotificationHandler<PublicAccessEntrySavedNotification>,
|
||||
INotificationHandler<PublicAccessEntryDeletedNotification>,
|
||||
INotificationHandler<UserSavedNotification>,
|
||||
INotificationHandler<UserDeletedNotification>,
|
||||
INotificationHandler<UserGroupWithUsersSavedNotification>,
|
||||
INotificationHandler<UserGroupDeletedNotification>,
|
||||
INotificationHandler<MemberGroupDeletedNotification>,
|
||||
INotificationHandler<MemberGroupSavedNotification>,
|
||||
INotificationHandler<DataTypeDeletedNotification>,
|
||||
INotificationHandler<DataTypeSavedNotification>
|
||||
{
|
||||
private List<Action> _unbinders;
|
||||
|
||||
@@ -49,30 +66,6 @@ namespace Umbraco.Cms.Core.Cache
|
||||
|
||||
_logger.LogInformation("Initializing Umbraco internal event handlers for cache refreshing.");
|
||||
|
||||
// bind to user and user group events
|
||||
Bind(() => UserService.SavedUserGroup += UserService_SavedUserGroup,
|
||||
() => UserService.SavedUserGroup -= UserService_SavedUserGroup);
|
||||
Bind(() => UserService.DeletedUserGroup += UserService_DeletedUserGroup,
|
||||
() => UserService.DeletedUserGroup -= UserService_DeletedUserGroup);
|
||||
Bind(() => UserService.SavedUser += UserService_SavedUser,
|
||||
() => UserService.SavedUser -= UserService_SavedUser);
|
||||
Bind(() => UserService.DeletedUser += UserService_DeletedUser,
|
||||
() => UserService.DeletedUser -= UserService_DeletedUser);
|
||||
Bind(() => UserService.UserGroupPermissionsAssigned += UserService_UserGroupPermissionsAssigned,
|
||||
() => UserService.UserGroupPermissionsAssigned -= UserService_UserGroupPermissionsAssigned);
|
||||
|
||||
// bind to dictionary events
|
||||
Bind(() => LocalizationService.DeletedDictionaryItem += LocalizationService_DeletedDictionaryItem,
|
||||
() => LocalizationService.DeletedDictionaryItem -= LocalizationService_DeletedDictionaryItem);
|
||||
Bind(() => LocalizationService.SavedDictionaryItem += LocalizationService_SavedDictionaryItem,
|
||||
() => LocalizationService.SavedDictionaryItem -= LocalizationService_SavedDictionaryItem);
|
||||
|
||||
// bind to data type events
|
||||
Bind(() => DataTypeService.Deleted += DataTypeService_Deleted,
|
||||
() => DataTypeService.Deleted -= DataTypeService_Deleted);
|
||||
Bind(() => DataTypeService.Saved += DataTypeService_Saved,
|
||||
() => DataTypeService.Saved -= DataTypeService_Saved);
|
||||
|
||||
// bind to stylesheet events
|
||||
Bind(() => FileService.SavedStylesheet += FileService_SavedStylesheet,
|
||||
() => FileService.SavedStylesheet -= FileService_SavedStylesheet);
|
||||
@@ -85,12 +78,6 @@ namespace Umbraco.Cms.Core.Cache
|
||||
Bind(() => DomainService.Deleted += DomainService_Deleted,
|
||||
() => DomainService.Deleted -= DomainService_Deleted);
|
||||
|
||||
// bind to language events
|
||||
Bind(() => LocalizationService.SavedLanguage += LocalizationService_SavedLanguage,
|
||||
() => LocalizationService.SavedLanguage -= LocalizationService_SavedLanguage);
|
||||
Bind(() => LocalizationService.DeletedLanguage += LocalizationService_DeletedLanguage,
|
||||
() => LocalizationService.DeletedLanguage -= LocalizationService_DeletedLanguage);
|
||||
|
||||
// bind to content type events
|
||||
Bind(() => ContentTypeService.Changed += ContentTypeService_Changed,
|
||||
() => ContentTypeService.Changed -= ContentTypeService_Changed);
|
||||
@@ -111,16 +98,6 @@ namespace Umbraco.Cms.Core.Cache
|
||||
Bind(() => MacroService.Deleted += MacroService_Deleted,
|
||||
() => MacroService.Deleted -= MacroService_Deleted);
|
||||
|
||||
// bind to member events
|
||||
Bind(() => MemberService.Saved += MemberService_Saved,
|
||||
() => MemberService.Saved -= MemberService_Saved);
|
||||
Bind(() => MemberService.Deleted += MemberService_Deleted,
|
||||
() => MemberService.Deleted -= MemberService_Deleted);
|
||||
Bind(() => MemberGroupService.Saved += MemberGroupService_Saved,
|
||||
() => MemberGroupService.Saved -= MemberGroupService_Saved);
|
||||
Bind(() => MemberGroupService.Deleted += MemberGroupService_Deleted,
|
||||
() => MemberGroupService.Deleted -= MemberGroupService_Deleted);
|
||||
|
||||
// bind to media events - handles all media changes
|
||||
Bind(() => MediaService.TreeChanged += MediaService_TreeChanged,
|
||||
() => MediaService.TreeChanged -= MediaService_TreeChanged);
|
||||
@@ -135,12 +112,6 @@ namespace Umbraco.Cms.Core.Cache
|
||||
//Bind(() => ContentService.DeletedBlueprint += ContentService_DeletedBlueprint,
|
||||
// () => ContentService.DeletedBlueprint -= ContentService_DeletedBlueprint);
|
||||
|
||||
// bind to public access events
|
||||
Bind(() => PublicAccessService.Saved += PublicAccessService_Saved,
|
||||
() => PublicAccessService.Saved -= PublicAccessService_Saved);
|
||||
Bind(() => PublicAccessService.Deleted += PublicAccessService_Deleted,
|
||||
() => PublicAccessService.Deleted -= PublicAccessService_Deleted);
|
||||
|
||||
// bind to relation type events
|
||||
Bind(() => RelationService.SavedRelationType += RelationService_SavedRelationType,
|
||||
() => RelationService.SavedRelationType -= RelationService_SavedRelationType);
|
||||
@@ -150,15 +121,15 @@ namespace Umbraco.Cms.Core.Cache
|
||||
|
||||
#region PublicAccessService
|
||||
|
||||
private void PublicAccessService_Saved(IPublicAccessService sender, SaveEventArgs<PublicAccessEntry> e)
|
||||
public void Handle(PublicAccessEntrySavedNotification notification)
|
||||
{
|
||||
|
||||
_distributedCache.RefreshPublicAccess();
|
||||
}
|
||||
|
||||
private void PublicAccessService_Deleted(IPublicAccessService sender, DeleteEventArgs<PublicAccessEntry> e)
|
||||
public void Handle(PublicAccessEntryDeletedNotification notification)
|
||||
{
|
||||
_distributedCache.RefreshPublicAccess();
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -196,33 +167,40 @@ namespace Umbraco.Cms.Core.Cache
|
||||
#endregion
|
||||
|
||||
#region LocalizationService / Dictionary
|
||||
|
||||
private void LocalizationService_SavedDictionaryItem(ILocalizationService sender, SaveEventArgs<IDictionaryItem> e)
|
||||
public void Handle(DictionaryItemSavedNotification notification)
|
||||
{
|
||||
foreach (var entity in e.SavedEntities)
|
||||
foreach (IDictionaryItem entity in notification.SavedEntities)
|
||||
{
|
||||
_distributedCache.RefreshDictionaryCache(entity.Id);
|
||||
}
|
||||
}
|
||||
|
||||
private void LocalizationService_DeletedDictionaryItem(ILocalizationService sender, DeleteEventArgs<IDictionaryItem> e)
|
||||
public void Handle(DictionaryItemDeletedNotification notification)
|
||||
{
|
||||
foreach (var entity in e.DeletedEntities)
|
||||
foreach (IDictionaryItem entity in notification.DeletedEntities)
|
||||
{
|
||||
_distributedCache.RemoveDictionaryCache(entity.Id);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region DataTypeService
|
||||
|
||||
private void DataTypeService_Saved(IDataTypeService sender, SaveEventArgs<IDataType> e)
|
||||
public void Handle(DataTypeSavedNotification notification)
|
||||
{
|
||||
foreach (var entity in e.SavedEntities)
|
||||
foreach (IDataType entity in notification.SavedEntities)
|
||||
{
|
||||
_distributedCache.RefreshDataTypeCache(entity);
|
||||
}
|
||||
}
|
||||
|
||||
private void DataTypeService_Deleted(IDataTypeService sender, DeleteEventArgs<IDataType> e)
|
||||
public void Handle(DataTypeDeletedNotification notification)
|
||||
{
|
||||
foreach (var entity in e.DeletedEntities)
|
||||
foreach (IDataType entity in notification.DeletedEntities)
|
||||
{
|
||||
_distributedCache.RemoveDataTypeCache(entity);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -248,23 +226,25 @@ namespace Umbraco.Cms.Core.Cache
|
||||
/// <summary>
|
||||
/// Fires when a language is deleted
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
private void LocalizationService_DeletedLanguage(ILocalizationService sender, DeleteEventArgs<ILanguage> e)
|
||||
/// <param name="notification"></param>
|
||||
public void Handle(LanguageDeletedNotification notification)
|
||||
{
|
||||
foreach (var entity in e.DeletedEntities)
|
||||
foreach (ILanguage entity in notification.DeletedEntities)
|
||||
{
|
||||
_distributedCache.RemoveLanguageCache(entity);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fires when a language is saved
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
private void LocalizationService_SavedLanguage(ILocalizationService sender, SaveEventArgs<ILanguage> e)
|
||||
/// <param name="notification"></param>
|
||||
public void Handle(LanguageSavedNotification notification)
|
||||
{
|
||||
foreach (var entity in e.SavedEntities)
|
||||
foreach (ILanguage entity in notification.SavedEntities)
|
||||
{
|
||||
_distributedCache.RefreshLanguageCache(entity);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -298,39 +278,36 @@ namespace Umbraco.Cms.Core.Cache
|
||||
|
||||
#region UserService
|
||||
|
||||
private void UserService_UserGroupPermissionsAssigned(IUserService sender, SaveEventArgs<EntityPermission> e)
|
||||
public void Handle(UserSavedNotification notification)
|
||||
{
|
||||
// TODO: Not sure if we need this yet depends if we start caching permissions
|
||||
//var groupIds = e.SavedEntities.Select(x => x.UserGroupId).Distinct();
|
||||
//foreach (var groupId in groupIds)
|
||||
//{
|
||||
// DistributedCache.Instance.RefreshUserGroupPermissionsCache(groupId);
|
||||
//}
|
||||
}
|
||||
|
||||
private void UserService_SavedUser(IUserService sender, SaveEventArgs<IUser> e)
|
||||
{
|
||||
foreach (var entity in e.SavedEntities)
|
||||
foreach (IUser entity in notification.SavedEntities)
|
||||
{
|
||||
_distributedCache.RefreshUserCache(entity.Id);
|
||||
}
|
||||
}
|
||||
|
||||
private void UserService_DeletedUser(IUserService sender, DeleteEventArgs<IUser> e)
|
||||
public void Handle(UserDeletedNotification notification)
|
||||
{
|
||||
foreach (var entity in e.DeletedEntities)
|
||||
foreach (IUser entity in notification.DeletedEntities)
|
||||
{
|
||||
_distributedCache.RemoveUserCache(entity.Id);
|
||||
}
|
||||
}
|
||||
|
||||
private void UserService_SavedUserGroup(IUserService sender, SaveEventArgs<UserGroupWithUsers> e)
|
||||
public void Handle(UserGroupWithUsersSavedNotification notification)
|
||||
{
|
||||
foreach (var entity in e.SavedEntities)
|
||||
foreach (UserGroupWithUsers entity in notification.SavedEntities)
|
||||
{
|
||||
_distributedCache.RefreshUserGroupCache(entity.UserGroup.Id);
|
||||
}
|
||||
}
|
||||
|
||||
private void UserService_DeletedUserGroup(IUserService sender, DeleteEventArgs<IUserGroup> e)
|
||||
public void Handle(UserGroupDeletedNotification notification)
|
||||
{
|
||||
|
||||
foreach (var entity in e.DeletedEntities)
|
||||
foreach (IUserGroup entity in notification.DeletedEntities)
|
||||
{
|
||||
_distributedCache.RemoveUserGroupCache(entity.Id);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -392,33 +369,41 @@ namespace Umbraco.Cms.Core.Cache
|
||||
|
||||
#region MemberService
|
||||
|
||||
private void MemberService_Deleted(IMemberService sender, DeleteEventArgs<IMember> e)
|
||||
public void Handle(MemberDeletedNotification notification)
|
||||
{
|
||||
_distributedCache.RemoveMemberCache(e.DeletedEntities.ToArray());
|
||||
_distributedCache.RemoveMemberCache(notification.DeletedEntities.ToArray());
|
||||
}
|
||||
|
||||
private void MemberService_Saved(IMemberService sender, SaveEventArgs<IMember> e)
|
||||
public void Handle(MemberSavedNotification notification)
|
||||
{
|
||||
_distributedCache.RefreshMemberCache(e.SavedEntities.ToArray());
|
||||
_distributedCache.RefreshMemberCache(notification.SavedEntities.ToArray());
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region MemberGroupService
|
||||
|
||||
private void MemberGroupService_Deleted(IMemberGroupService sender, DeleteEventArgs<IMemberGroup> e)
|
||||
/// <summary>
|
||||
/// Fires when a member group is deleted
|
||||
/// </summary>
|
||||
/// <param name="notification"></param>
|
||||
public void Handle(MemberGroupDeletedNotification notification)
|
||||
{
|
||||
foreach (var m in e.DeletedEntities.ToArray())
|
||||
foreach (IMemberGroup entity in notification.DeletedEntities)
|
||||
{
|
||||
_distributedCache.RemoveMemberGroupCache(m.Id);
|
||||
_distributedCache.RemoveMemberGroupCache(entity.Id);
|
||||
}
|
||||
}
|
||||
|
||||
private void MemberGroupService_Saved(IMemberGroupService sender, SaveEventArgs<IMemberGroup> e)
|
||||
/// <summary>
|
||||
/// Fires when a member group is saved
|
||||
/// </summary>
|
||||
/// <param name="notification"></param>
|
||||
public void Handle(MemberGroupSavedNotification notification)
|
||||
{
|
||||
foreach (var m in e.SavedEntities.ToArray())
|
||||
foreach (IMemberGroup entity in notification.SavedEntities)
|
||||
{
|
||||
_distributedCache.RemoveMemberGroupCache(m.Id);
|
||||
_distributedCache.RemoveMemberGroupCache(entity.Id);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
using Umbraco.Cms.Core.Composing;
|
||||
|
||||
namespace Umbraco.Cms.Core.Compose
|
||||
{
|
||||
public sealed class AuditEventsComposer : ComponentComposer<AuditEventsComponent>, ICoreComposer
|
||||
{ }
|
||||
}
|
||||
@@ -2,7 +2,6 @@ using System;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Umbraco.Cms.Core.Composing;
|
||||
using Umbraco.Cms.Core.Configuration.Models;
|
||||
using Umbraco.Cms.Core.Events;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
@@ -10,12 +9,21 @@ using Umbraco.Cms.Core.Models.Membership;
|
||||
using Umbraco.Cms.Core.Net;
|
||||
using Umbraco.Cms.Core.Security;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.Core.Services.Implement;
|
||||
using Umbraco.Cms.Infrastructure.Services.Notifications;
|
||||
using Umbraco.Extensions;
|
||||
|
||||
namespace Umbraco.Cms.Core.Compose
|
||||
{
|
||||
public sealed class AuditEventsComponent : IComponent
|
||||
public sealed class AuditNotificationsHandler :
|
||||
INotificationHandler<MemberSavedNotification>,
|
||||
INotificationHandler<MemberDeletedNotification>,
|
||||
INotificationHandler<AssignedMemberRolesNotification>,
|
||||
INotificationHandler<RemovedMemberRolesNotification>,
|
||||
INotificationHandler<ExportedMemberNotification>,
|
||||
INotificationHandler<UserSavedNotification>,
|
||||
INotificationHandler<UserDeletedNotification>,
|
||||
INotificationHandler<UserGroupWithUsersSavedNotification>,
|
||||
INotificationHandler<AssignedUserGroupPermissionsNotification>
|
||||
{
|
||||
private readonly IAuditService _auditService;
|
||||
private readonly IUserService _userService;
|
||||
@@ -23,55 +31,26 @@ namespace Umbraco.Cms.Core.Compose
|
||||
private readonly IIpResolver _ipResolver;
|
||||
private readonly IBackOfficeSecurityAccessor _backOfficeSecurityAccessor;
|
||||
private readonly GlobalSettings _globalSettings;
|
||||
private readonly IMemberService _memberService;
|
||||
|
||||
public AuditEventsComponent(
|
||||
public AuditNotificationsHandler(
|
||||
IAuditService auditService,
|
||||
IUserService userService,
|
||||
IEntityService entityService,
|
||||
IIpResolver ipResolver,
|
||||
IOptions<GlobalSettings> globalSettings,
|
||||
IBackOfficeSecurityAccessor backOfficeSecurityAccessor)
|
||||
IBackOfficeSecurityAccessor backOfficeSecurityAccessor,
|
||||
IMemberService memberService)
|
||||
{
|
||||
_auditService = auditService;
|
||||
_userService = userService;
|
||||
_entityService = entityService;
|
||||
_ipResolver = ipResolver;
|
||||
_backOfficeSecurityAccessor = backOfficeSecurityAccessor;
|
||||
_memberService = memberService;
|
||||
_globalSettings = globalSettings.Value;
|
||||
}
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
UserService.SavedUserGroup += OnSavedUserGroupWithUsers;
|
||||
|
||||
UserService.SavedUser += OnSavedUser;
|
||||
UserService.DeletedUser += OnDeletedUser;
|
||||
UserService.UserGroupPermissionsAssigned += UserGroupPermissionAssigned;
|
||||
|
||||
MemberService.Saved += OnSavedMember;
|
||||
MemberService.Deleted += OnDeletedMember;
|
||||
MemberService.AssignedRoles += OnAssignedRoles;
|
||||
MemberService.RemovedRoles += OnRemovedRoles;
|
||||
MemberService.Exported += OnMemberExported;
|
||||
}
|
||||
|
||||
public void Terminate()
|
||||
{
|
||||
UserService.SavedUserGroup -= OnSavedUserGroupWithUsers;
|
||||
|
||||
UserService.SavedUser -= OnSavedUser;
|
||||
UserService.DeletedUser -= OnDeletedUser;
|
||||
UserService.UserGroupPermissionsAssigned -= UserGroupPermissionAssigned;
|
||||
|
||||
MemberService.Saved -= OnSavedMember;
|
||||
MemberService.Deleted -= OnDeletedMember;
|
||||
MemberService.AssignedRoles -= OnAssignedRoles;
|
||||
MemberService.RemovedRoles -= OnRemovedRoles;
|
||||
MemberService.Exported -= OnMemberExported;
|
||||
}
|
||||
|
||||
public static IUser UnknownUser(GlobalSettings globalSettings) => new User(globalSettings) { Id = Cms.Core.Constants.Security.UnknownUserId, Name = Cms.Core.Constants.Security.UnknownUserName, Email = "" };
|
||||
|
||||
private IUser CurrentPerformingUser
|
||||
{
|
||||
get
|
||||
@@ -82,45 +61,48 @@ namespace Umbraco.Cms.Core.Compose
|
||||
}
|
||||
}
|
||||
|
||||
private IUser GetPerformingUser(int userId)
|
||||
{
|
||||
var found = userId >= 0 ? _userService.GetUserById(userId) : null;
|
||||
return found ?? UnknownUser(_globalSettings);
|
||||
}
|
||||
public static IUser UnknownUser(GlobalSettings globalSettings) => new User(globalSettings) { Id = Cms.Core.Constants.Security.UnknownUserId, Name = Cms.Core.Constants.Security.UnknownUserName, Email = "" };
|
||||
|
||||
private string PerformingIp => _ipResolver.GetCurrentRequestIpAddress();
|
||||
|
||||
private string FormatEmail(IMember member)
|
||||
{
|
||||
return member == null ? string.Empty : member.Email.IsNullOrWhiteSpace() ? "" : $"<{member.Email}>";
|
||||
}
|
||||
private string FormatEmail(IMember member) => member == null ? string.Empty : member.Email.IsNullOrWhiteSpace() ? "" : $"<{member.Email}>";
|
||||
|
||||
private string FormatEmail(IUser user)
|
||||
{
|
||||
return user == null ? string.Empty : user.Email.IsNullOrWhiteSpace() ? "" : $"<{user.Email}>";
|
||||
}
|
||||
private string FormatEmail(IUser user) => user == null ? string.Empty : user.Email.IsNullOrWhiteSpace() ? "" : $"<{user.Email}>";
|
||||
|
||||
private void OnRemovedRoles(IMemberService sender, RolesEventArgs args)
|
||||
public void Handle(MemberSavedNotification notification)
|
||||
{
|
||||
var performingUser = CurrentPerformingUser;
|
||||
var roles = string.Join(", ", args.Roles);
|
||||
var members = sender.GetAllMembers(args.MemberIds).ToDictionary(x => x.Id, x => x);
|
||||
foreach (var id in args.MemberIds)
|
||||
var members = notification.SavedEntities;
|
||||
foreach (var member in members)
|
||||
{
|
||||
members.TryGetValue(id, out var member);
|
||||
var dp = string.Join(", ", ((Member)member).GetWereDirtyProperties());
|
||||
|
||||
_auditService.Write(performingUser.Id, $"User \"{performingUser.Name}\" {FormatEmail(performingUser)}", PerformingIp,
|
||||
DateTime.UtcNow,
|
||||
-1, $"Member {id} \"{member?.Name ?? "(unknown)"}\" {FormatEmail(member)}",
|
||||
"umbraco/member/roles/removed", $"roles modified, removed {roles}");
|
||||
-1, $"Member {member.Id} \"{member.Name}\" {FormatEmail(member)}",
|
||||
"umbraco/member/save", $"updating {(string.IsNullOrWhiteSpace(dp) ? "(nothing)" : dp)}");
|
||||
}
|
||||
}
|
||||
|
||||
private void OnAssignedRoles(IMemberService sender, RolesEventArgs args)
|
||||
public void Handle(MemberDeletedNotification notification)
|
||||
{
|
||||
var performingUser = CurrentPerformingUser;
|
||||
var roles = string.Join(", ", args.Roles);
|
||||
var members = sender.GetAllMembers(args.MemberIds).ToDictionary(x => x.Id, x => x);
|
||||
foreach (var id in args.MemberIds)
|
||||
var members = notification.DeletedEntities;
|
||||
foreach (var member in members)
|
||||
{
|
||||
_auditService.Write(performingUser.Id, $"User \"{performingUser.Name}\" {FormatEmail(performingUser)}", PerformingIp,
|
||||
DateTime.UtcNow,
|
||||
-1, $"Member {member.Id} \"{member.Name}\" {FormatEmail(member)}",
|
||||
"umbraco/member/delete", $"delete member id:{member.Id} \"{member.Name}\" {FormatEmail(member)}");
|
||||
}
|
||||
}
|
||||
|
||||
public void Handle(AssignedMemberRolesNotification notification)
|
||||
{
|
||||
var performingUser = CurrentPerformingUser;
|
||||
var roles = string.Join(", ", notification.Roles);
|
||||
var members = _memberService.GetAllMembers(notification.MemberIds).ToDictionary(x => x.Id, x => x);
|
||||
foreach (var id in notification.MemberIds)
|
||||
{
|
||||
members.TryGetValue(id, out var member);
|
||||
_auditService.Write(performingUser.Id, $"User \"{performingUser.Name}\" {FormatEmail(performingUser)}", PerformingIp,
|
||||
@@ -130,10 +112,25 @@ namespace Umbraco.Cms.Core.Compose
|
||||
}
|
||||
}
|
||||
|
||||
private void OnMemberExported(IMemberService sender, ExportedMemberEventArgs exportedMemberEventArgs)
|
||||
public void Handle(RemovedMemberRolesNotification notification)
|
||||
{
|
||||
var performingUser = CurrentPerformingUser;
|
||||
var member = exportedMemberEventArgs.Member;
|
||||
var roles = string.Join(", ", notification.Roles);
|
||||
var members = _memberService.GetAllMembers(notification.MemberIds).ToDictionary(x => x.Id, x => x);
|
||||
foreach (var id in notification.MemberIds)
|
||||
{
|
||||
members.TryGetValue(id, out var member);
|
||||
_auditService.Write(performingUser.Id, $"User \"{performingUser.Name}\" {FormatEmail(performingUser)}", PerformingIp,
|
||||
DateTime.UtcNow,
|
||||
-1, $"Member {id} \"{member?.Name ?? "(unknown)"}\" {FormatEmail(member)}",
|
||||
"umbraco/member/roles/removed", $"roles modified, removed {roles}");
|
||||
}
|
||||
}
|
||||
|
||||
public void Handle(ExportedMemberNotification notification)
|
||||
{
|
||||
var performingUser = CurrentPerformingUser;
|
||||
var member = notification.Member;
|
||||
|
||||
_auditService.Write(performingUser.Id, $"User \"{performingUser.Name}\" {FormatEmail(performingUser)}", PerformingIp,
|
||||
DateTime.UtcNow,
|
||||
@@ -141,10 +138,40 @@ namespace Umbraco.Cms.Core.Compose
|
||||
"umbraco/member/exported", "exported member data");
|
||||
}
|
||||
|
||||
private void OnSavedUserGroupWithUsers(IUserService sender, SaveEventArgs<UserGroupWithUsers> saveEventArgs)
|
||||
public void Handle(UserSavedNotification notification)
|
||||
{
|
||||
var performingUser = CurrentPerformingUser;
|
||||
foreach (var groupWithUser in saveEventArgs.SavedEntities)
|
||||
var affectedUsers = notification.SavedEntities;
|
||||
foreach (var affectedUser in affectedUsers)
|
||||
{
|
||||
var groups = affectedUser.WasPropertyDirty("Groups")
|
||||
? string.Join(", ", affectedUser.Groups.Select(x => x.Alias))
|
||||
: null;
|
||||
|
||||
var dp = string.Join(", ", ((User)affectedUser).GetWereDirtyProperties());
|
||||
|
||||
_auditService.Write(performingUser.Id, $"User \"{performingUser.Name}\" {FormatEmail(performingUser)}", PerformingIp,
|
||||
DateTime.UtcNow,
|
||||
affectedUser.Id, $"User \"{affectedUser.Name}\" {FormatEmail(affectedUser)}",
|
||||
"umbraco/user/save", $"updating {(string.IsNullOrWhiteSpace(dp) ? "(nothing)" : dp)}{(groups == null ? "" : "; groups assigned: " + groups)}");
|
||||
}
|
||||
}
|
||||
|
||||
public void Handle(UserDeletedNotification notification)
|
||||
{
|
||||
var performingUser = CurrentPerformingUser;
|
||||
var affectedUsers = notification.DeletedEntities;
|
||||
foreach (var affectedUser in affectedUsers)
|
||||
_auditService.Write(performingUser.Id, $"User \"{performingUser.Name}\" {FormatEmail(performingUser)}", PerformingIp,
|
||||
DateTime.UtcNow,
|
||||
affectedUser.Id, $"User \"{affectedUser.Name}\" {FormatEmail(affectedUser)}",
|
||||
"umbraco/user/delete", "delete user");
|
||||
}
|
||||
|
||||
public void Handle(UserGroupWithUsersSavedNotification notification)
|
||||
{
|
||||
var performingUser = CurrentPerformingUser;
|
||||
foreach (var groupWithUser in notification.SavedEntities)
|
||||
{
|
||||
var group = groupWithUser.UserGroup;
|
||||
|
||||
@@ -192,13 +219,13 @@ namespace Umbraco.Cms.Core.Compose
|
||||
}
|
||||
}
|
||||
|
||||
private void UserGroupPermissionAssigned(IUserService sender, SaveEventArgs<EntityPermission> saveEventArgs)
|
||||
public void Handle(AssignedUserGroupPermissionsNotification notification)
|
||||
{
|
||||
var performingUser = CurrentPerformingUser;
|
||||
var perms = saveEventArgs.SavedEntities;
|
||||
var perms = notification.EntityPermissions;
|
||||
foreach (var perm in perms)
|
||||
{
|
||||
var group = sender.GetUserGroupById(perm.UserGroupId);
|
||||
var group = _userService.GetUserGroupById(perm.UserGroupId);
|
||||
var assigned = string.Join(", ", perm.AssignedPermissions);
|
||||
var entity = _entityService.Get(perm.EntityId);
|
||||
|
||||
@@ -208,100 +235,5 @@ namespace Umbraco.Cms.Core.Compose
|
||||
"umbraco/user-group/permissions-change", $"assigning {(string.IsNullOrWhiteSpace(assigned) ? "(nothing)" : assigned)} on id:{perm.EntityId} \"{entity.Name}\"");
|
||||
}
|
||||
}
|
||||
|
||||
private void OnSavedMember(IMemberService sender, SaveEventArgs<IMember> saveEventArgs)
|
||||
{
|
||||
var performingUser = CurrentPerformingUser;
|
||||
var members = saveEventArgs.SavedEntities;
|
||||
foreach (var member in members)
|
||||
{
|
||||
var dp = string.Join(", ", ((Member) member).GetWereDirtyProperties());
|
||||
|
||||
_auditService.Write(performingUser.Id, $"User \"{performingUser.Name}\" {FormatEmail(performingUser)}", PerformingIp,
|
||||
DateTime.UtcNow,
|
||||
-1, $"Member {member.Id} \"{member.Name}\" {FormatEmail(member)}",
|
||||
"umbraco/member/save", $"updating {(string.IsNullOrWhiteSpace(dp) ? "(nothing)" : dp)}");
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDeletedMember(IMemberService sender, DeleteEventArgs<IMember> deleteEventArgs)
|
||||
{
|
||||
var performingUser = CurrentPerformingUser;
|
||||
var members = deleteEventArgs.DeletedEntities;
|
||||
foreach (var member in members)
|
||||
{
|
||||
_auditService.Write(performingUser.Id, $"User \"{performingUser.Name}\" {FormatEmail(performingUser)}", PerformingIp,
|
||||
DateTime.UtcNow,
|
||||
-1, $"Member {member.Id} \"{member.Name}\" {FormatEmail(member)}",
|
||||
"umbraco/member/delete", $"delete member id:{member.Id} \"{member.Name}\" {FormatEmail(member)}");
|
||||
}
|
||||
}
|
||||
|
||||
private void OnSavedUser(IUserService sender, SaveEventArgs<IUser> saveEventArgs)
|
||||
{
|
||||
var performingUser = CurrentPerformingUser;
|
||||
var affectedUsers = saveEventArgs.SavedEntities;
|
||||
foreach (var affectedUser in affectedUsers)
|
||||
{
|
||||
var groups = affectedUser.WasPropertyDirty("Groups")
|
||||
? string.Join(", ", affectedUser.Groups.Select(x => x.Alias))
|
||||
: null;
|
||||
|
||||
var dp = string.Join(", ", ((User)affectedUser).GetWereDirtyProperties());
|
||||
|
||||
_auditService.Write(performingUser.Id, $"User \"{performingUser.Name}\" {FormatEmail(performingUser)}", PerformingIp,
|
||||
DateTime.UtcNow,
|
||||
affectedUser.Id, $"User \"{affectedUser.Name}\" {FormatEmail(affectedUser)}",
|
||||
"umbraco/user/save", $"updating {(string.IsNullOrWhiteSpace(dp) ? "(nothing)" : dp)}{(groups == null ? "" : "; groups assigned: " + groups)}");
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDeletedUser(IUserService sender, DeleteEventArgs<IUser> deleteEventArgs)
|
||||
{
|
||||
var performingUser = CurrentPerformingUser;
|
||||
var affectedUsers = deleteEventArgs.DeletedEntities;
|
||||
foreach (var affectedUser in affectedUsers)
|
||||
_auditService.Write(performingUser.Id, $"User \"{performingUser.Name}\" {FormatEmail(performingUser)}", PerformingIp,
|
||||
DateTime.UtcNow,
|
||||
affectedUser.Id, $"User \"{affectedUser.Name}\" {FormatEmail(affectedUser)}",
|
||||
"umbraco/user/delete", "delete user");
|
||||
}
|
||||
|
||||
private void WriteAudit(int performingId, int affectedId, string ipAddress, string eventType, string eventDetails, string affectedDetails = null)
|
||||
{
|
||||
var performingUser = _userService.GetUserById(performingId);
|
||||
|
||||
var performingDetails = performingUser == null
|
||||
? $"User UNKNOWN:{performingId}"
|
||||
: $"User \"{performingUser.Name}\" {FormatEmail(performingUser)}";
|
||||
|
||||
WriteAudit(performingId, performingDetails, affectedId, ipAddress, eventType, eventDetails, affectedDetails);
|
||||
}
|
||||
|
||||
private void WriteAudit(IUser performingUser, int affectedId, string ipAddress, string eventType, string eventDetails)
|
||||
{
|
||||
var performingDetails = performingUser == null
|
||||
? $"User UNKNOWN"
|
||||
: $"User \"{performingUser.Name}\" {FormatEmail(performingUser)}";
|
||||
|
||||
WriteAudit(performingUser?.Id ?? 0, performingDetails, affectedId, ipAddress, eventType, eventDetails);
|
||||
}
|
||||
|
||||
private void WriteAudit(int performingId, string performingDetails, int affectedId, string ipAddress, string eventType, string eventDetails, string affectedDetails = null)
|
||||
{
|
||||
if (affectedDetails == null)
|
||||
{
|
||||
var affectedUser = _userService.GetUserById(affectedId);
|
||||
affectedDetails = affectedUser == null
|
||||
? $"User UNKNOWN:{affectedId}"
|
||||
: $"User \"{affectedUser.Name}\" {FormatEmail(affectedUser)}";
|
||||
}
|
||||
|
||||
_auditService.Write(performingId, performingDetails,
|
||||
ipAddress,
|
||||
DateTime.UtcNow,
|
||||
affectedId, affectedDetails,
|
||||
eventType, eventDetails);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,181 +0,0 @@
|
||||
// Copyright (c) Umbraco.
|
||||
// See LICENSE for more details.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Umbraco.Cms.Core.Actions;
|
||||
using Umbraco.Cms.Core.Composing;
|
||||
using Umbraco.Cms.Core.Configuration.Models;
|
||||
using Umbraco.Cms.Core.Events;
|
||||
using Umbraco.Cms.Core.Hosting;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Models.Membership;
|
||||
using Umbraco.Cms.Core.Security;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.Core.Services.Implement;
|
||||
using Umbraco.Extensions;
|
||||
|
||||
namespace Umbraco.Cms.Core.Compose
|
||||
{
|
||||
/// <remarks>
|
||||
/// TODO: this component must be removed entirely - there is some code duplication in <see cref="UserNotificationsHandler"/> in anticipation of this component being deleted
|
||||
/// </remarks>
|
||||
public sealed class NotificationsComponent : IComponent
|
||||
{
|
||||
private readonly Notifier _notifier;
|
||||
private readonly ActionCollection _actions;
|
||||
private readonly IContentService _contentService;
|
||||
|
||||
public NotificationsComponent(Notifier notifier, ActionCollection actions, IContentService contentService)
|
||||
{
|
||||
_notifier = notifier;
|
||||
_actions = actions;
|
||||
_contentService = contentService;
|
||||
}
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
//Send notifications for the public access changed action
|
||||
PublicAccessService.Saved += PublicAccessService_Saved;
|
||||
|
||||
UserService.UserGroupPermissionsAssigned += UserService_UserGroupPermissionsAssigned;
|
||||
}
|
||||
|
||||
public void Terminate()
|
||||
{
|
||||
PublicAccessService.Saved -= PublicAccessService_Saved;
|
||||
UserService.UserGroupPermissionsAssigned -= UserService_UserGroupPermissionsAssigned;
|
||||
}
|
||||
|
||||
private void UserService_UserGroupPermissionsAssigned(IUserService sender, SaveEventArgs<EntityPermission> args)
|
||||
=> UserServiceUserGroupPermissionsAssigned(args, _contentService);
|
||||
|
||||
private void PublicAccessService_Saved(IPublicAccessService sender, SaveEventArgs<PublicAccessEntry> args)
|
||||
=> PublicAccessServiceSaved(args, _contentService);
|
||||
|
||||
private void UserServiceUserGroupPermissionsAssigned(SaveEventArgs<EntityPermission> args, IContentService contentService)
|
||||
{
|
||||
var entities = contentService.GetByIds(args.SavedEntities.Select(e => e.EntityId)).ToArray();
|
||||
if (entities.Any() == false)
|
||||
{
|
||||
return;
|
||||
}
|
||||
_notifier.Notify(_actions.GetAction<ActionRights>(), entities);
|
||||
}
|
||||
|
||||
private void PublicAccessServiceSaved(SaveEventArgs<PublicAccessEntry> args, IContentService contentService)
|
||||
{
|
||||
var entities = contentService.GetByIds(args.SavedEntities.Select(e => e.ProtectedNodeId)).ToArray();
|
||||
if (entities.Any() == false)
|
||||
{
|
||||
return;
|
||||
}
|
||||
_notifier.Notify(_actions.GetAction<ActionProtect>(), entities);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This class is used to send the notifications
|
||||
/// </summary>
|
||||
public sealed class Notifier
|
||||
{
|
||||
private readonly IBackOfficeSecurityAccessor _backOfficeSecurityAccessor;
|
||||
private readonly IHostingEnvironment _hostingEnvironment;
|
||||
private readonly INotificationService _notificationService;
|
||||
private readonly IUserService _userService;
|
||||
private readonly ILocalizedTextService _textService;
|
||||
private readonly GlobalSettings _globalSettings;
|
||||
private readonly ILogger<Notifier> _logger;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Notifier"/> class.
|
||||
/// </summary>
|
||||
public Notifier(
|
||||
IBackOfficeSecurityAccessor backOfficeSecurityAccessor,
|
||||
IHostingEnvironment hostingEnvironment,
|
||||
INotificationService notificationService,
|
||||
IUserService userService,
|
||||
ILocalizedTextService textService,
|
||||
IOptions<GlobalSettings> globalSettings,
|
||||
ILogger<Notifier> logger)
|
||||
{
|
||||
_backOfficeSecurityAccessor = backOfficeSecurityAccessor;
|
||||
_hostingEnvironment = hostingEnvironment;
|
||||
_notificationService = notificationService;
|
||||
_userService = userService;
|
||||
_textService = textService;
|
||||
_globalSettings = globalSettings.Value;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public void Notify(IAction action, params IContent[] entities)
|
||||
{
|
||||
var user = _backOfficeSecurityAccessor?.BackOfficeSecurity?.CurrentUser;
|
||||
|
||||
//if there is no current user, then use the admin
|
||||
if (user == null)
|
||||
{
|
||||
_logger.LogDebug("There is no current Umbraco user logged in, the notifications will be sent from the administrator");
|
||||
user = _userService.GetUserById(Constants.Security.SuperUserId);
|
||||
if (user == null)
|
||||
{
|
||||
_logger.LogWarning("Notifications can not be sent, no admin user with id {SuperUserId} could be resolved", Constants.Security.SuperUserId);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
SendNotification(user, entities, action, _hostingEnvironment.ApplicationMainUrl);
|
||||
}
|
||||
|
||||
private void SendNotification(IUser sender, IEnumerable<IContent> entities, IAction action, Uri siteUri)
|
||||
{
|
||||
if (sender == null) throw new ArgumentNullException(nameof(sender));
|
||||
if (siteUri == null)
|
||||
{
|
||||
_logger.LogWarning("Notifications can not be sent, no site URL is set (might be during boot process?)");
|
||||
return;
|
||||
}
|
||||
|
||||
//group by the content type variation since the emails will be different
|
||||
foreach(var contentVariantGroup in entities.GroupBy(x => x.ContentType.Variations))
|
||||
{
|
||||
_notificationService.SendNotifications(
|
||||
sender,
|
||||
contentVariantGroup,
|
||||
action.Letter.ToString(CultureInfo.InvariantCulture),
|
||||
_textService.Localize("actions", action.Alias),
|
||||
siteUri,
|
||||
((IUser user, NotificationEmailSubjectParams subject) x)
|
||||
=> _textService.Localize(
|
||||
"notifications/mailSubject",
|
||||
x.user.GetUserCulture(_textService, _globalSettings),
|
||||
new[] { x.subject.SiteUrl, x.subject.Action, x.subject.ItemName }),
|
||||
((IUser user, NotificationEmailBodyParams body, bool isHtml) x)
|
||||
=> _textService.Localize(
|
||||
x.isHtml ? "notifications/mailBodyHtml" : "notifications/mailBody",
|
||||
x.user.GetUserCulture(_textService, _globalSettings),
|
||||
new[]
|
||||
{
|
||||
x.body.RecipientName,
|
||||
x.body.Action,
|
||||
x.body.ItemName,
|
||||
x.body.EditedUser,
|
||||
x.body.SiteUrl,
|
||||
x.body.ItemId,
|
||||
//format the summary depending on if it's variant or not
|
||||
contentVariantGroup.Key == ContentVariation.Culture
|
||||
? (x.isHtml ? _textService.Localize("notifications/mailBodyVariantHtmlSummary", new[]{ x.body.Summary }) : _textService.Localize("notifications/mailBodyVariantSummary", new []{ x.body.Summary }))
|
||||
: x.body.Summary,
|
||||
x.body.ItemUrl
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,10 +1,12 @@
|
||||
// Copyright (c) Umbraco.
|
||||
// See LICENSE for more details.
|
||||
|
||||
using Umbraco.Cms.Core.Cache;
|
||||
using Umbraco.Cms.Core.Composing;
|
||||
using Umbraco.Cms.Core.DependencyInjection;
|
||||
using Umbraco.Cms.Core.Events;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Models.Membership;
|
||||
using Umbraco.Cms.Core.PropertyEditors;
|
||||
using Umbraco.Cms.Core.Routing;
|
||||
using Umbraco.Cms.Infrastructure.Services.Notifications;
|
||||
@@ -12,14 +14,10 @@ using Umbraco.Extensions;
|
||||
|
||||
namespace Umbraco.Cms.Core.Compose
|
||||
{
|
||||
public sealed class NotificationsComposer : ComponentComposer<NotificationsComponent>, ICoreComposer
|
||||
public sealed class NotificationsComposer : ICoreComposer
|
||||
{
|
||||
public override void Compose(IUmbracoBuilder builder)
|
||||
public void Compose(IUmbracoBuilder builder)
|
||||
{
|
||||
base.Compose(builder);
|
||||
|
||||
builder.Services.AddUnique<NotificationsComponent.Notifier>();
|
||||
|
||||
// add handlers for sending user notifications (i.e. emails)
|
||||
builder.Services.AddUnique<UserNotificationsHandler.Notifier>();
|
||||
builder
|
||||
@@ -31,7 +29,9 @@ namespace Umbraco.Cms.Core.Compose
|
||||
.AddNotificationHandler<ContentCopiedNotification, UserNotificationsHandler>()
|
||||
.AddNotificationHandler<ContentRolledBackNotification, UserNotificationsHandler>()
|
||||
.AddNotificationHandler<ContentSentToPublishNotification, UserNotificationsHandler>()
|
||||
.AddNotificationHandler<ContentUnpublishedNotification, UserNotificationsHandler>();
|
||||
.AddNotificationHandler<ContentUnpublishedNotification, UserNotificationsHandler>()
|
||||
.AddNotificationHandler<AssignedUserGroupPermissionsNotification, UserNotificationsHandler>()
|
||||
.AddNotificationHandler<PublicAccessEntrySavedNotification, UserNotificationsHandler>();
|
||||
|
||||
// add handlers for building content relations
|
||||
builder
|
||||
@@ -51,10 +51,12 @@ namespace Umbraco.Cms.Core.Compose
|
||||
.AddNotificationHandler<ContentDeletedNotification, FileUploadPropertyEditor>()
|
||||
.AddNotificationHandler<MediaDeletedNotification, FileUploadPropertyEditor>()
|
||||
.AddNotificationHandler<MediaSavingNotification, FileUploadPropertyEditor>()
|
||||
.AddNotificationHandler<MemberDeletedNotification, FileUploadPropertyEditor>()
|
||||
.AddNotificationHandler<ContentCopiedNotification, ImageCropperPropertyEditor>()
|
||||
.AddNotificationHandler<ContentDeletedNotification, ImageCropperPropertyEditor>()
|
||||
.AddNotificationHandler<MediaDeletedNotification, ImageCropperPropertyEditor>()
|
||||
.AddNotificationHandler<MediaSavingNotification, ImageCropperPropertyEditor>();
|
||||
.AddNotificationHandler<MediaSavingNotification, ImageCropperPropertyEditor>()
|
||||
.AddNotificationHandler<MemberDeletedNotification, ImageCropperPropertyEditor>();
|
||||
|
||||
// add notification handlers for redirect tracking
|
||||
builder
|
||||
@@ -62,6 +64,37 @@ namespace Umbraco.Cms.Core.Compose
|
||||
.AddNotificationHandler<ContentPublishedNotification, RedirectTrackingHandler>()
|
||||
.AddNotificationHandler<ContentMovingNotification, RedirectTrackingHandler>()
|
||||
.AddNotificationHandler<ContentMovedNotification, RedirectTrackingHandler>();
|
||||
|
||||
// Add notification handlers for DistributedCache
|
||||
builder
|
||||
.AddNotificationHandler<DictionaryItemDeletedNotification, DistributedCacheBinder>()
|
||||
.AddNotificationHandler<DictionaryItemSavedNotification, DistributedCacheBinder>()
|
||||
.AddNotificationHandler<LanguageSavedNotification, DistributedCacheBinder>()
|
||||
.AddNotificationHandler<LanguageDeletedNotification, DistributedCacheBinder>()
|
||||
.AddNotificationHandler<MemberSavedNotification, DistributedCacheBinder>()
|
||||
.AddNotificationHandler<MemberDeletedNotification, DistributedCacheBinder>()
|
||||
.AddNotificationHandler<PublicAccessEntrySavedNotification, DistributedCacheBinder>()
|
||||
.AddNotificationHandler<PublicAccessEntryDeletedNotification, DistributedCacheBinder>()
|
||||
.AddNotificationHandler<UserSavedNotification, DistributedCacheBinder>()
|
||||
.AddNotificationHandler<UserDeletedNotification, DistributedCacheBinder>()
|
||||
.AddNotificationHandler<UserGroupWithUsersSavedNotification, DistributedCacheBinder>()
|
||||
.AddNotificationHandler<UserGroupDeletedNotification, DistributedCacheBinder>()
|
||||
.AddNotificationHandler<MemberGroupDeletedNotification, DistributedCacheBinder>()
|
||||
.AddNotificationHandler<MemberGroupSavedNotification, DistributedCacheBinder>()
|
||||
.AddNotificationHandler<DataTypeDeletedNotification, DistributedCacheBinder>()
|
||||
.AddNotificationHandler<DataTypeSavedNotification, DistributedCacheBinder>();
|
||||
|
||||
// add notification handlers for auditing
|
||||
builder
|
||||
.AddNotificationHandler<MemberSavedNotification, AuditNotificationsHandler>()
|
||||
.AddNotificationHandler<MemberDeletedNotification, AuditNotificationsHandler>()
|
||||
.AddNotificationHandler<AssignedMemberRolesNotification, AuditNotificationsHandler>()
|
||||
.AddNotificationHandler<RemovedMemberRolesNotification, AuditNotificationsHandler>()
|
||||
.AddNotificationHandler<ExportedMemberNotification, AuditNotificationsHandler>()
|
||||
.AddNotificationHandler<UserSavedNotification, AuditNotificationsHandler>()
|
||||
.AddNotificationHandler<UserDeletedNotification, AuditNotificationsHandler>()
|
||||
.AddNotificationHandler<UserGroupWithUsersSavedNotification, AuditNotificationsHandler>()
|
||||
.AddNotificationHandler<AssignedUserGroupPermissionsNotification, AuditNotificationsHandler>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,17 @@
|
||||
using Umbraco.Cms.Core.Composing;
|
||||
using Umbraco.Cms.Core.Composing;
|
||||
using Umbraco.Cms.Core.DependencyInjection;
|
||||
using Umbraco.Cms.Infrastructure.Services.Notifications;
|
||||
|
||||
namespace Umbraco.Cms.Core.Compose
|
||||
{
|
||||
/// <summary>
|
||||
/// Used to ensure that the public access data file is kept up to date properly
|
||||
/// </summary>
|
||||
public sealed class PublicAccessComposer : ComponentComposer<PublicAccessComponent>, ICoreComposer
|
||||
{ }
|
||||
public sealed class PublicAccessComposer : ICoreComposer
|
||||
{
|
||||
public void Compose(IUmbracoBuilder builder) =>
|
||||
builder
|
||||
.AddNotificationHandler<MemberGroupSavedNotification, PublicAccessHandler>()
|
||||
.AddNotificationHandler<MemberGroupDeletedNotification, PublicAccessHandler>();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,34 +1,29 @@
|
||||
using System;
|
||||
using Umbraco.Cms.Core.Composing;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Umbraco.Cms.Core.Events;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.Core.Services.Implement;
|
||||
using Umbraco.Cms.Infrastructure.Services.Notifications;
|
||||
using Umbraco.Extensions;
|
||||
|
||||
namespace Umbraco.Cms.Core.Compose
|
||||
{
|
||||
public sealed class PublicAccessComponent : IComponent
|
||||
public sealed class PublicAccessHandler :
|
||||
INotificationHandler<MemberGroupSavedNotification>,
|
||||
INotificationHandler<MemberGroupDeletedNotification>
|
||||
{
|
||||
private readonly IPublicAccessService _publicAccessService;
|
||||
public PublicAccessComponent(IPublicAccessService publicAccessService)
|
||||
{
|
||||
|
||||
public PublicAccessHandler(IPublicAccessService publicAccessService) =>
|
||||
_publicAccessService = publicAccessService ?? throw new ArgumentNullException(nameof(publicAccessService));
|
||||
}
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
MemberGroupService.Saved += MemberGroupService_Saved;
|
||||
}
|
||||
public void Handle(MemberGroupSavedNotification notification) => Handle(notification.SavedEntities);
|
||||
|
||||
public void Terminate()
|
||||
{
|
||||
MemberGroupService.Saved -= MemberGroupService_Saved;
|
||||
}
|
||||
public void Handle(MemberGroupDeletedNotification notification) => Handle(notification.DeletedEntities);
|
||||
|
||||
private void MemberGroupService_Saved(IMemberGroupService sender, SaveEventArgs<IMemberGroup> e)
|
||||
private void Handle(IEnumerable<IMemberGroup> affectedEntities)
|
||||
{
|
||||
foreach (var grp in e.SavedEntities)
|
||||
foreach (var grp in affectedEntities)
|
||||
{
|
||||
//check if the name has changed
|
||||
if (grp.AdditionalData.ContainsKey("previousName")
|
||||
@@ -29,7 +29,9 @@ namespace Umbraco.Cms.Core.Events
|
||||
INotificationHandler<ContentCopiedNotification>,
|
||||
INotificationHandler<ContentRolledBackNotification>,
|
||||
INotificationHandler<ContentSentToPublishNotification>,
|
||||
INotificationHandler<ContentUnpublishedNotification>
|
||||
INotificationHandler<ContentUnpublishedNotification>,
|
||||
INotificationHandler<AssignedUserGroupPermissionsNotification>,
|
||||
INotificationHandler<PublicAccessEntrySavedNotification>
|
||||
{
|
||||
private readonly Notifier _notifier;
|
||||
private readonly ActionCollection _actions;
|
||||
@@ -209,7 +211,26 @@ namespace Umbraco.Cms.Core.Events
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Handle(AssignedUserGroupPermissionsNotification notification)
|
||||
{
|
||||
var entities = _contentService.GetByIds(notification.EntityPermissions.Select(e => e.EntityId)).ToArray();
|
||||
if (entities.Any() == false)
|
||||
{
|
||||
return;
|
||||
}
|
||||
_notifier.Notify(_actions.GetAction<ActionRights>(), entities);
|
||||
}
|
||||
|
||||
public void Handle(PublicAccessEntrySavedNotification notification)
|
||||
{
|
||||
var entities = _contentService.GetByIds(notification.SavedEntities.Select(e => e.ProtectedNodeId)).ToArray();
|
||||
if (entities.Any() == false)
|
||||
{
|
||||
return;
|
||||
}
|
||||
_notifier.Notify(_actions.GetAction<ActionProtect>(), entities);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,15 +12,18 @@ using Umbraco.Cms.Core.Scoping;
|
||||
using Umbraco.Cms.Infrastructure.Persistence.Dtos;
|
||||
using Umbraco.Cms.Infrastructure.Persistence.Factories;
|
||||
using Umbraco.Cms.Infrastructure.Persistence.Querying;
|
||||
using Umbraco.Cms.Infrastructure.Services.Notifications;
|
||||
using Umbraco.Extensions;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement
|
||||
{
|
||||
internal class MemberGroupRepository : EntityRepositoryBase<int, IMemberGroup>, IMemberGroupRepository
|
||||
{
|
||||
public MemberGroupRepository(IScopeAccessor scopeAccessor, AppCaches cache, ILogger<MemberGroupRepository> logger)
|
||||
: base(scopeAccessor, cache, logger)
|
||||
{ }
|
||||
private readonly IEventMessagesFactory _eventMessagesFactory;
|
||||
|
||||
public MemberGroupRepository(IScopeAccessor scopeAccessor, AppCaches cache, ILogger<MemberGroupRepository> logger, IEventMessagesFactory eventMessagesFactory)
|
||||
: base(scopeAccessor, cache, logger) =>
|
||||
_eventMessagesFactory = eventMessagesFactory;
|
||||
|
||||
protected override IMemberGroup PerformGet(int id)
|
||||
{
|
||||
@@ -156,10 +159,14 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement
|
||||
};
|
||||
PersistNewItem(grp);
|
||||
|
||||
if (AmbientScope.Events.DispatchCancelable(SavingMemberGroup, this, new SaveEventArgs<IMemberGroup>(grp)))
|
||||
var evtMsgs = _eventMessagesFactory.Get();
|
||||
if (AmbientScope.Notifications.PublishCancelable(new MemberGroupSavingNotification(grp, evtMsgs)))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
AmbientScope.Notifications.Publish(new MemberGroupSavedNotification(grp, evtMsgs));
|
||||
|
||||
AmbientScope.Events.Dispatch(SavedMemberGroup, this, new SaveEventArgs<IMemberGroup>(grp));
|
||||
return grp;
|
||||
}
|
||||
|
||||
@@ -240,13 +247,16 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement
|
||||
var missingRoles = roleNames.Except(existingRoles, StringComparer.CurrentCultureIgnoreCase);
|
||||
var missingGroups = missingRoles.Select(x => new MemberGroup {Name = x}).ToArray();
|
||||
|
||||
if (AmbientScope.Events.DispatchCancelable(SavingMemberGroup, this, new SaveEventArgs<IMemberGroup>(missingGroups)))
|
||||
var evtMsgs = _eventMessagesFactory.Get();
|
||||
if (AmbientScope.Notifications.PublishCancelable(new MemberGroupSavingNotification(missingGroups, evtMsgs)))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var m in missingGroups)
|
||||
PersistNewItem(m);
|
||||
|
||||
AmbientScope.Events.Dispatch(SavedMemberGroup, this, new SaveEventArgs<IMemberGroup>(missingGroups));
|
||||
AmbientScope.Notifications.Publish(new MemberGroupSavedNotification(missingGroups, evtMsgs));
|
||||
|
||||
//now go get all the dto's for roles with these role names
|
||||
var rolesForNames = Database.Fetch<NodeDto>(existingSql).ToArray();
|
||||
@@ -310,17 +320,5 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement
|
||||
[Column("MemberGroup")]
|
||||
public int MemberGroupId { get; set; }
|
||||
}
|
||||
|
||||
// TODO: understand why we need these two repository-level events, move them back to service
|
||||
|
||||
/// <summary>
|
||||
/// Occurs before Save
|
||||
/// </summary>
|
||||
internal static event TypedEventHandler<IMemberGroupRepository, SaveEventArgs<IMemberGroup>> SavingMemberGroup;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs after Save
|
||||
/// </summary>
|
||||
internal static event TypedEventHandler<IMemberGroupRepository, SaveEventArgs<IMemberGroup>> SavedMemberGroup;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -314,7 +314,6 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement
|
||||
// persist the member dto
|
||||
dto.NodeId = nodeDto.NodeId;
|
||||
|
||||
// TODO: password parts of this file need updating
|
||||
// if the password is empty, generate one with the special prefix
|
||||
// this will hash the guid with a salt so should be nicely random
|
||||
if (entity.RawPasswordValue.IsNullOrWhiteSpace())
|
||||
|
||||
@@ -27,7 +27,8 @@ namespace Umbraco.Cms.Core.PropertyEditors
|
||||
Icon = "icon-download-alt")]
|
||||
public class FileUploadPropertyEditor : DataEditor, IMediaUrlGenerator,
|
||||
INotificationHandler<ContentCopiedNotification>, INotificationHandler<ContentDeletedNotification>,
|
||||
INotificationHandler<MediaDeletedNotification>, INotificationHandler<MediaSavingNotification>
|
||||
INotificationHandler<MediaDeletedNotification>, INotificationHandler<MediaSavingNotification>,
|
||||
INotificationHandler<MemberDeletedNotification>
|
||||
{
|
||||
private readonly IMediaFileSystem _mediaFileSystem;
|
||||
private readonly ContentSettings _contentSettings;
|
||||
@@ -95,10 +96,7 @@ namespace Umbraco.Cms.Core.PropertyEditors
|
||||
/// The paths to all file upload property files contained within a collection of content entities
|
||||
/// </summary>
|
||||
/// <param name="entities"></param>
|
||||
/// <remarks>
|
||||
/// This method must be made private once MemberService events have been replaced by notifications
|
||||
/// </remarks>
|
||||
internal IEnumerable<string> ContainedFilePaths(IEnumerable<IContentBase> entities) => entities
|
||||
private IEnumerable<string> ContainedFilePaths(IEnumerable<IContentBase> entities) => entities
|
||||
.SelectMany(x => x.Properties)
|
||||
.Where(IsUploadField)
|
||||
.SelectMany(GetFilePathsFromPropertyValues)
|
||||
@@ -162,6 +160,8 @@ namespace Umbraco.Cms.Core.PropertyEditors
|
||||
|
||||
public void Handle(MediaDeletedNotification notification) => DeleteContainedFiles(notification.DeletedEntities);
|
||||
|
||||
public void Handle(MemberDeletedNotification notification) => DeleteContainedFiles(notification.DeletedEntities);
|
||||
|
||||
private void DeleteContainedFiles(IEnumerable<IContentBase> deletedEntities)
|
||||
{
|
||||
var filePathsToDelete = ContainedFilePaths(deletedEntities);
|
||||
|
||||
@@ -202,8 +202,8 @@ namespace Umbraco.Cms.Core.PropertyEditors
|
||||
_richTextPropertyValueEditor.GetReferences(x.Value)))
|
||||
yield return umbracoEntityReference;
|
||||
|
||||
foreach (var umbracoEntityReference in mediaValues.Where(x=>x.Value.HasValues).SelectMany(x =>
|
||||
_mediaPickerPropertyValueEditor.GetReferences(x.Value["udi"])))
|
||||
foreach (var umbracoEntityReference in mediaValues.Where(x=>x.Value.HasValues)
|
||||
.SelectMany(x => _mediaPickerPropertyValueEditor.GetReferences(x.Value["udi"])))
|
||||
yield return umbracoEntityReference;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,7 +34,8 @@ namespace Umbraco.Cms.Core.PropertyEditors
|
||||
Icon = "icon-crop")]
|
||||
public class ImageCropperPropertyEditor : DataEditor, IMediaUrlGenerator,
|
||||
INotificationHandler<ContentCopiedNotification>, INotificationHandler<ContentDeletedNotification>,
|
||||
INotificationHandler<MediaDeletedNotification>, INotificationHandler<MediaSavingNotification>
|
||||
INotificationHandler<MediaDeletedNotification>, INotificationHandler<MediaSavingNotification>,
|
||||
INotificationHandler<MemberDeletedNotification>
|
||||
{
|
||||
private readonly IMediaFileSystem _mediaFileSystem;
|
||||
private readonly ContentSettings _contentSettings;
|
||||
@@ -131,10 +132,7 @@ namespace Umbraco.Cms.Core.PropertyEditors
|
||||
/// The paths to all image cropper property files contained within a collection of content entities
|
||||
/// </summary>
|
||||
/// <param name="entities"></param>
|
||||
/// <remarks>
|
||||
/// This method must be made private once MemberService events have been replaced by notifications
|
||||
/// </remarks>
|
||||
internal IEnumerable<string> ContainedFilePaths(IEnumerable<IContentBase> entities) => entities
|
||||
private IEnumerable<string> ContainedFilePaths(IEnumerable<IContentBase> entities) => entities
|
||||
.SelectMany(x => x.Properties)
|
||||
.Where(IsCropperField)
|
||||
.SelectMany(GetFilePathsFromPropertyValues)
|
||||
@@ -218,6 +216,8 @@ namespace Umbraco.Cms.Core.PropertyEditors
|
||||
|
||||
public void Handle(MediaDeletedNotification notification) => DeleteContainedFiles(notification.DeletedEntities);
|
||||
|
||||
public void Handle(MemberDeletedNotification notification) => DeleteContainedFiles(notification.DeletedEntities);
|
||||
|
||||
private void DeleteContainedFiles(IEnumerable<IContentBase> deletedEntities)
|
||||
{
|
||||
var filePathsToDelete = ContainedFilePaths(deletedEntities);
|
||||
|
||||
@@ -1,56 +0,0 @@
|
||||
// Copyright (c) Umbraco.
|
||||
// See LICENSE for more details.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Umbraco.Cms.Core.Composing;
|
||||
using Umbraco.Cms.Core.Events;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.Core.Services.Implement;
|
||||
|
||||
namespace Umbraco.Cms.Core.PropertyEditors
|
||||
{
|
||||
// TODO: delete this component and make the "ContainedFilePaths" methods on FileUploadPropertyEditor and ImageCropperPropertyEditor private once MemberService uses notifications instead of static events
|
||||
public sealed class PropertyEditorsComponent : IComponent
|
||||
{
|
||||
private readonly PropertyEditorCollection _propertyEditors;
|
||||
private readonly List<Action> _terminate = new List<Action>();
|
||||
|
||||
public PropertyEditorsComponent(PropertyEditorCollection propertyEditors)
|
||||
{
|
||||
_propertyEditors = propertyEditors;
|
||||
}
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
var fileUpload = _propertyEditors.OfType<FileUploadPropertyEditor>().FirstOrDefault();
|
||||
if (fileUpload != null) Initialize(fileUpload);
|
||||
|
||||
var imageCropper = _propertyEditors.OfType<ImageCropperPropertyEditor>().FirstOrDefault();
|
||||
if (imageCropper != null) Initialize(imageCropper);
|
||||
|
||||
// grid/examine moved to ExamineComponent
|
||||
}
|
||||
|
||||
public void Terminate()
|
||||
{
|
||||
foreach (var t in _terminate) t();
|
||||
}
|
||||
|
||||
private void Initialize(FileUploadPropertyEditor fileUpload)
|
||||
{
|
||||
void memberServiceDeleted(IMemberService sender, DeleteEventArgs<IMember> args) => args.MediaFilesToDelete.AddRange(fileUpload.ContainedFilePaths(args.DeletedEntities.Cast<ContentBase>()));
|
||||
MemberService.Deleted += memberServiceDeleted;
|
||||
_terminate.Add(() => MemberService.Deleted -= memberServiceDeleted);
|
||||
}
|
||||
|
||||
private void Initialize(ImageCropperPropertyEditor imageCropper)
|
||||
{
|
||||
void memberServiceDeleted(IMemberService sender, DeleteEventArgs<IMember> args) => args.MediaFilesToDelete.AddRange(imageCropper.ContainedFilePaths(args.DeletedEntities.Cast<ContentBase>()));
|
||||
MemberService.Deleted += memberServiceDeleted;
|
||||
_terminate.Add(() => MemberService.Deleted -= memberServiceDeleted);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
// Copyright (c) Umbraco.
|
||||
// See LICENSE for more details.
|
||||
|
||||
using Umbraco.Cms.Core.Composing;
|
||||
|
||||
namespace Umbraco.Cms.Core.PropertyEditors
|
||||
{
|
||||
public sealed class PropertyEditorsComposer : ComponentComposer<PropertyEditorsComponent>, ICoreComposer
|
||||
{ }
|
||||
}
|
||||
@@ -23,20 +23,29 @@ namespace Umbraco.Cms.Core.Security
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <remarks>
|
||||
/// Returns a ClaimsIdentity that has the required claims, and allows flowing of claims from external identity
|
||||
/// </remarks>
|
||||
public override async Task<ClaimsPrincipal> CreateAsync(BackOfficeIdentityUser user)
|
||||
{
|
||||
if (user == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(user));
|
||||
}
|
||||
protected virtual string AuthenticationType { get; } = Constants.Security.BackOfficeAuthenticationType;
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override async Task<ClaimsIdentity> GenerateClaimsAsync(BackOfficeIdentityUser user)
|
||||
{
|
||||
// NOTE: Have a look at the base implementation https://github.com/dotnet/aspnetcore/blob/master/src/Identity/Extensions.Core/src/UserClaimsPrincipalFactory.cs#L79
|
||||
// since it's setting an authentication type which is not what we want.
|
||||
// so we override this method to change it.
|
||||
|
||||
// get the base
|
||||
ClaimsIdentity baseIdentity = await base.GenerateClaimsAsync(user);
|
||||
|
||||
baseIdentity.AddRequiredClaims(
|
||||
// now create a new one with the correct authentication type
|
||||
var id = new ClaimsIdentity(
|
||||
AuthenticationType,
|
||||
Options.ClaimsIdentity.UserNameClaimType,
|
||||
Options.ClaimsIdentity.RoleClaimType);
|
||||
|
||||
// and merge all others from the base implementation
|
||||
id.MergeAllClaims(baseIdentity);
|
||||
|
||||
// ensure our required claims are there
|
||||
id.AddRequiredClaims(
|
||||
user.Id,
|
||||
user.UserName,
|
||||
user.Name,
|
||||
@@ -49,23 +58,9 @@ namespace Umbraco.Cms.Core.Security
|
||||
|
||||
// now we can flow any custom claims that the actual user has currently
|
||||
// assigned which could be done in the OnExternalLogin callback
|
||||
baseIdentity.MergeClaimsFromBackOfficeIdentity(user);
|
||||
id.MergeClaimsFromBackOfficeIdentity(user);
|
||||
|
||||
return new ClaimsPrincipal(baseIdentity);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override async Task<ClaimsIdentity> GenerateClaimsAsync(BackOfficeIdentityUser user)
|
||||
{
|
||||
// TODO: Have a look at the base implementation https://github.com/dotnet/aspnetcore/blob/master/src/Identity/Extensions.Core/src/UserClaimsPrincipalFactory.cs#L79
|
||||
// since it's setting an authentication type that is probably not what we want.
|
||||
// also, this is the method that we should be returning our UmbracoBackOfficeIdentity from , not the method above,
|
||||
// the method above just returns a principal that wraps the identity and we dont use a custom principal,
|
||||
// see https://github.com/dotnet/aspnetcore/blob/master/src/Identity/Extensions.Core/src/UserClaimsPrincipalFactory.cs#L66
|
||||
|
||||
ClaimsIdentity identity = await base.GenerateClaimsAsync(user);
|
||||
|
||||
return identity;
|
||||
return id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,15 @@ namespace Umbraco.Extensions
|
||||
// is re-issued and we don't want to merge old values of these.
|
||||
private static readonly string[] s_ignoredClaims = new[] { ClaimTypes.CookiePath, Constants.Security.SessionIdClaimType };
|
||||
|
||||
public static void MergeAllClaims(this ClaimsIdentity destination, ClaimsIdentity source)
|
||||
{
|
||||
foreach (Claim claim in source.Claims
|
||||
.Where(claim => !destination.HasClaim(claim.Type, claim.Value)))
|
||||
{
|
||||
destination.AddClaim(new Claim(claim.Type, claim.Value));
|
||||
}
|
||||
}
|
||||
|
||||
public static void MergeClaimsFromBackOfficeIdentity(this ClaimsIdentity destination, ClaimsIdentity source)
|
||||
{
|
||||
foreach (Claim claim in source.Claims
|
||||
|
||||
@@ -3,7 +3,7 @@ namespace Umbraco.Cms.Core.Security
|
||||
/// <summary>
|
||||
/// The user manager for members
|
||||
/// </summary>
|
||||
public interface IMemberManager : IUmbracoUserManager<MembersIdentityUser>
|
||||
public interface IMemberManager : IUmbracoUserManager<MemberIdentityUser>
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,10 +48,10 @@ namespace Umbraco.Cms.Core.Security
|
||||
target.EnableChangeTracking();
|
||||
});
|
||||
|
||||
mapper.Define<IMember, MembersIdentityUser>(
|
||||
mapper.Define<IMember, MemberIdentityUser>(
|
||||
(source, context) =>
|
||||
{
|
||||
var target = new MembersIdentityUser(source.Id);
|
||||
var target = new MemberIdentityUser(source.Id);
|
||||
target.DisableChangeTracking();
|
||||
return target;
|
||||
},
|
||||
@@ -100,7 +100,7 @@ namespace Umbraco.Cms.Core.Security
|
||||
//target.Roles =;
|
||||
}
|
||||
|
||||
private void Map(IMember source, MembersIdentityUser target)
|
||||
private void Map(IMember source, MemberIdentityUser target)
|
||||
{
|
||||
target.Email = source.Email;
|
||||
target.UserName = source.Username;
|
||||
|
||||
@@ -5,18 +5,18 @@ using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace Umbraco.Cms.Core.Security
|
||||
{
|
||||
public class MembersIdentityBuilder : IdentityBuilder
|
||||
public class MemberIdentityBuilder : IdentityBuilder
|
||||
{
|
||||
public MembersIdentityBuilder(IServiceCollection services) : base(typeof(MembersIdentityUser), services)
|
||||
public MemberIdentityBuilder(IServiceCollection services) : base(typeof(MemberIdentityUser), services)
|
||||
{
|
||||
}
|
||||
|
||||
public MembersIdentityBuilder(Type role, IServiceCollection services) : base(typeof(MembersIdentityUser), role, services)
|
||||
public MemberIdentityBuilder(Type role, IServiceCollection services) : base(typeof(MemberIdentityUser), role, services)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a token provider for the <seealso cref="MembersIdentityUser"/>.
|
||||
/// Adds a token provider for the <seealso cref="MemberIdentityUser"/>.
|
||||
/// </summary>
|
||||
/// <param name="providerName">The name of the provider to add.</param>
|
||||
/// <param name="provider">The type of the <see cref="IUserTwoFactorTokenProvider{UmbracoMembersIdentityUser}"/> to add.</param>
|
||||
@@ -27,7 +27,7 @@ namespace Umbraco.Cms.Core.Security
|
||||
{
|
||||
throw new InvalidOperationException($"Invalid Type for TokenProvider: {provider.FullName}");
|
||||
}
|
||||
Services.Configure<MembersIdentityOptions>(options =>
|
||||
Services.Configure<MemberIdentityOptions>(options =>
|
||||
{
|
||||
options.Tokens.ProviderMap[providerName] = new TokenProviderDescriptor(provider);
|
||||
});
|
||||
@@ -5,7 +5,7 @@ namespace Umbraco.Cms.Core.Security
|
||||
/// <summary>
|
||||
/// Identity options specifically for the Umbraco members identity implementation
|
||||
/// </summary>
|
||||
public class MembersIdentityOptions : IdentityOptions
|
||||
public class MemberIdentityOptions : IdentityOptions
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -11,7 +11,7 @@ namespace Umbraco.Cms.Core.Security
|
||||
/// <summary>
|
||||
/// The identity user used for the member
|
||||
/// </summary>
|
||||
public class MembersIdentityUser : UmbracoIdentityUser
|
||||
public class MemberIdentityUser : UmbracoIdentityUser
|
||||
{
|
||||
private string _name;
|
||||
private string _passwordConfig;
|
||||
@@ -23,29 +23,29 @@ namespace Umbraco.Cms.Core.Security
|
||||
groups => groups.GetHashCode());
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="MembersIdentityUser"/> class.
|
||||
/// Initializes a new instance of the <see cref="MemberIdentityUser"/> class.
|
||||
/// </summary>
|
||||
public MembersIdentityUser(int userId)
|
||||
public MemberIdentityUser(int userId)
|
||||
{
|
||||
// use the property setters - they do more than just setting a field
|
||||
Id = UserIdToString(userId);
|
||||
}
|
||||
|
||||
public MembersIdentityUser()
|
||||
public MemberIdentityUser()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Used to construct a new instance without an identity
|
||||
/// </summary>
|
||||
public static MembersIdentityUser CreateNew(string username, string email, string memberTypeAlias, string name = null)
|
||||
public static MemberIdentityUser CreateNew(string username, string email, string memberTypeAlias, string name = null)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(username))
|
||||
{
|
||||
throw new ArgumentException("Value cannot be null or whitespace.", nameof(username));
|
||||
}
|
||||
|
||||
var user = new MembersIdentityUser();
|
||||
var user = new MemberIdentityUser();
|
||||
user.DisableChangeTracking();
|
||||
user.UserName = username;
|
||||
user.Email = email;
|
||||
272
src/Umbraco.Infrastructure/Security/MemberRoleStore.cs
Normal file
272
src/Umbraco.Infrastructure/Security/MemberRoleStore.cs
Normal file
@@ -0,0 +1,272 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Models.Identity;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
|
||||
namespace Umbraco.Cms.Core.Security
|
||||
{
|
||||
/// <summary>
|
||||
/// A custom user store that uses Umbraco member data
|
||||
/// </summary>
|
||||
public class MemberRoleStore : IRoleStore<UmbracoIdentityRole>
|
||||
{
|
||||
private readonly IMemberGroupService _memberGroupService;
|
||||
private bool _disposed;
|
||||
|
||||
//TODO: Move into custom error describer.
|
||||
//TODO: How revealing can the error messages be?
|
||||
private readonly IdentityError _intParseError = new IdentityError { Code = "IdentityIdParseError", Description = "Cannot parse ID to int" };
|
||||
private readonly IdentityError _memberGroupNotFoundError = new IdentityError { Code = "IdentityMemberGroupNotFound", Description = "Member group not found" };
|
||||
private const string genericIdentityErrorCode = "IdentityErrorUserStore";
|
||||
|
||||
public MemberRoleStore(IMemberGroupService memberGroupService, IdentityErrorDescriber errorDescriber)
|
||||
{
|
||||
_memberGroupService = memberGroupService ?? throw new ArgumentNullException(nameof(memberGroupService));
|
||||
ErrorDescriber = errorDescriber ?? throw new ArgumentNullException(nameof(errorDescriber));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="IdentityErrorDescriber"/> for any error that occurred with the current operation.
|
||||
/// </summary>
|
||||
public IdentityErrorDescriber ErrorDescriber { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<IdentityResult> CreateAsync(UmbracoIdentityRole role, CancellationToken cancellationToken = default)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
ThrowIfDisposed();
|
||||
|
||||
if (role == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(role));
|
||||
}
|
||||
|
||||
var memberGroup = new MemberGroup
|
||||
{
|
||||
Name = role.Name
|
||||
};
|
||||
|
||||
_memberGroupService.Save(memberGroup);
|
||||
|
||||
role.Id = memberGroup.Id.ToString();
|
||||
|
||||
return Task.FromResult(IdentityResult.Success);
|
||||
}
|
||||
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<IdentityResult> UpdateAsync(UmbracoIdentityRole role, CancellationToken cancellationToken = default)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
ThrowIfDisposed();
|
||||
|
||||
if (role == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(role));
|
||||
}
|
||||
|
||||
if (!int.TryParse(role.Id, out int roleId))
|
||||
{
|
||||
return Task.FromResult(IdentityResult.Failed(_intParseError));
|
||||
}
|
||||
|
||||
IMemberGroup memberGroup = _memberGroupService.GetById(roleId);
|
||||
if (memberGroup != null)
|
||||
{
|
||||
if (MapToMemberGroup(role, memberGroup))
|
||||
{
|
||||
_memberGroupService.Save(memberGroup);
|
||||
}
|
||||
|
||||
return Task.FromResult(IdentityResult.Success);
|
||||
}
|
||||
else
|
||||
{
|
||||
return Task.FromResult(IdentityResult.Failed(_memberGroupNotFoundError));
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<IdentityResult> DeleteAsync(UmbracoIdentityRole role, CancellationToken cancellationToken = default)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
ThrowIfDisposed();
|
||||
|
||||
if (role == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(role));
|
||||
}
|
||||
|
||||
if (!int.TryParse(role.Id, out int roleId))
|
||||
{
|
||||
throw new ArgumentException("The Id of the role is not an integer");
|
||||
}
|
||||
|
||||
IMemberGroup memberGroup = _memberGroupService.GetById(roleId);
|
||||
if (memberGroup != null)
|
||||
{
|
||||
_memberGroupService.Delete(memberGroup);
|
||||
}
|
||||
else
|
||||
{
|
||||
return Task.FromResult(IdentityResult.Failed(_memberGroupNotFoundError));
|
||||
}
|
||||
|
||||
return Task.FromResult(IdentityResult.Success);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<string> GetRoleIdAsync(UmbracoIdentityRole role, CancellationToken cancellationToken = default)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
ThrowIfDisposed();
|
||||
|
||||
if (role == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(role));
|
||||
}
|
||||
|
||||
return Task.FromResult(role.Id);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<string> GetRoleNameAsync(UmbracoIdentityRole role, CancellationToken cancellationToken = default)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
ThrowIfDisposed();
|
||||
|
||||
if (role == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(role));
|
||||
}
|
||||
|
||||
return Task.FromResult(role.Name);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task SetRoleNameAsync(UmbracoIdentityRole role, string roleName, CancellationToken cancellationToken = default)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
ThrowIfDisposed();
|
||||
if (role == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(role));
|
||||
}
|
||||
role.Name = roleName;
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<string> GetNormalizedRoleNameAsync(UmbracoIdentityRole role, CancellationToken cancellationToken = default)
|
||||
=> GetRoleNameAsync(role, cancellationToken);
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task SetNormalizedRoleNameAsync(UmbracoIdentityRole role, string normalizedName, CancellationToken cancellationToken = default)
|
||||
=> SetRoleNameAsync(role, normalizedName, cancellationToken);
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<UmbracoIdentityRole> FindByIdAsync(string roleId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
ThrowIfDisposed();
|
||||
|
||||
if (string.IsNullOrWhiteSpace(roleId))
|
||||
{
|
||||
throw new ArgumentNullException(nameof(roleId));
|
||||
}
|
||||
|
||||
IMemberGroup memberGroup;
|
||||
|
||||
// member group can be found by int or Guid, so try both
|
||||
if (!int.TryParse(roleId, out int id))
|
||||
{
|
||||
if (!Guid.TryParse(roleId, out Guid guid))
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(roleId), $"{nameof(roleId)} is not a valid Guid");
|
||||
}
|
||||
else
|
||||
{
|
||||
memberGroup = _memberGroupService.GetById(guid);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
memberGroup = _memberGroupService.GetById(id);
|
||||
}
|
||||
|
||||
return Task.FromResult(memberGroup == null ? null : MapFromMemberGroup(memberGroup));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<UmbracoIdentityRole> FindByNameAsync(string name, CancellationToken cancellationToken = default)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
ThrowIfDisposed();
|
||||
|
||||
if (string.IsNullOrWhiteSpace(name))
|
||||
{
|
||||
throw new ArgumentNullException(nameof(name));
|
||||
}
|
||||
IMemberGroup memberGroup = _memberGroupService.GetByName(name);
|
||||
return Task.FromResult(memberGroup == null ? null : MapFromMemberGroup(memberGroup));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Maps a member group to an identity role
|
||||
/// </summary>
|
||||
/// <param name="memberGroup"></param>
|
||||
/// <returns></returns>
|
||||
private UmbracoIdentityRole MapFromMemberGroup(IMemberGroup memberGroup)
|
||||
{
|
||||
var result = new UmbracoIdentityRole
|
||||
{
|
||||
Id = memberGroup.Id.ToString(),
|
||||
Name = memberGroup.Name
|
||||
// TODO: Implement this functionality, requires DB and logic updates
|
||||
//ConcurrencyStamp
|
||||
};
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Map an identity role to a member group
|
||||
/// </summary>
|
||||
/// <param name="role"></param>
|
||||
/// <param name="memberGroup"></param>
|
||||
/// <returns></returns>
|
||||
private bool MapToMemberGroup(UmbracoIdentityRole role, IMemberGroup memberGroup)
|
||||
{
|
||||
var anythingChanged = false;
|
||||
|
||||
if (role.IsPropertyDirty(nameof(UmbracoIdentityRole.Name))
|
||||
&& !string.IsNullOrEmpty(role.Name) && memberGroup.Name != role.Name)
|
||||
{
|
||||
// TODO: Need to support ConcurrencyStamp and logic
|
||||
|
||||
memberGroup.Name = role.Name;
|
||||
anythingChanged = true;
|
||||
}
|
||||
|
||||
return anythingChanged;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dispose the store
|
||||
/// </summary>
|
||||
public void Dispose() => _disposed = true;
|
||||
|
||||
/// <summary>
|
||||
/// Throws if this class has been disposed.
|
||||
/// </summary>
|
||||
protected void ThrowIfDisposed()
|
||||
{
|
||||
if (_disposed)
|
||||
{
|
||||
throw new ObjectDisposedException(GetType().Name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Security.Claims;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Umbraco.Cms.Core.Scoping;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
|
||||
namespace Umbraco.Cms.Core.Security
|
||||
{
|
||||
/// <summary>
|
||||
/// A custom user store that uses Umbraco member data
|
||||
/// </summary>
|
||||
public class MemberRolesUserStore : RoleStoreBase<IdentityRole<string>, string, IdentityUserRole<string>, IdentityRoleClaim<string>>
|
||||
{
|
||||
private readonly IMemberService _memberService;
|
||||
private readonly IMemberGroupService _memberGroupService;
|
||||
private readonly IScopeProvider _scopeProvider;
|
||||
|
||||
public MemberRolesUserStore(IMemberService memberService, IMemberGroupService memberGroupService, IScopeProvider scopeProvider, IdentityErrorDescriber describer)
|
||||
: base(describer)
|
||||
{
|
||||
_memberService = memberService ?? throw new ArgumentNullException(nameof(memberService));
|
||||
_memberGroupService = memberGroupService ?? throw new ArgumentNullException(nameof(memberGroupService));
|
||||
_scopeProvider = scopeProvider ?? throw new ArgumentNullException(nameof(scopeProvider));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override IQueryable<IdentityRole<string>> Roles { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Task<IdentityResult> CreateAsync(IdentityRole<string> role, CancellationToken cancellationToken = new CancellationToken()) => throw new System.NotImplementedException();
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Task<IdentityResult> UpdateAsync(IdentityRole<string> role, CancellationToken cancellationToken = new CancellationToken()) => throw new System.NotImplementedException();
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Task<IdentityResult> DeleteAsync(IdentityRole<string> role, CancellationToken cancellationToken = new CancellationToken()) => throw new System.NotImplementedException();
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Task<IdentityRole<string>> FindByIdAsync(string id, CancellationToken cancellationToken = new CancellationToken()) => throw new System.NotImplementedException();
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Task<IdentityRole<string>> FindByNameAsync(string normalizedName, CancellationToken cancellationToken = new CancellationToken()) => throw new System.NotImplementedException();
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Task<IList<Claim>> GetClaimsAsync(IdentityRole<string> role, CancellationToken cancellationToken = new CancellationToken()) => throw new System.NotImplementedException();
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Task AddClaimAsync(IdentityRole<string> role, Claim claim, CancellationToken cancellationToken = new CancellationToken()) => throw new System.NotImplementedException();
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Task RemoveClaimAsync(IdentityRole<string> role, Claim claim, CancellationToken cancellationToken = new CancellationToken()) => throw new System.NotImplementedException();
|
||||
}
|
||||
}
|
||||
@@ -19,20 +19,21 @@ namespace Umbraco.Cms.Core.Security
|
||||
/// <summary>
|
||||
/// A custom user store that uses Umbraco member data
|
||||
/// </summary>
|
||||
public class MembersUserStore : UserStoreBase<MembersIdentityUser, IdentityRole<string>, string, IdentityUserClaim<string>, IdentityUserRole<string>, IdentityUserLogin<string>, IdentityUserToken<string>, IdentityRoleClaim<string>>
|
||||
public class MemberUserStore : UserStoreBase<MemberIdentityUser, UmbracoIdentityRole, string, IdentityUserClaim<string>, IdentityUserRole<string>, IdentityUserLogin<string>, IdentityUserToken<string>, IdentityRoleClaim<string>>
|
||||
{
|
||||
private const string genericIdentityErrorCode = "IdentityErrorUserStore";
|
||||
private readonly IMemberService _memberService;
|
||||
private readonly UmbracoMapper _mapper;
|
||||
private readonly IScopeProvider _scopeProvider;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="MembersUserStore"/> class for the members identity store
|
||||
/// Initializes a new instance of the <see cref="MemberUserStore"/> class for the members identity store
|
||||
/// </summary>
|
||||
/// <param name="memberService">The member service</param>
|
||||
/// <param name="mapper">The mapper for properties</param>
|
||||
/// <param name="scopeProvider">The scope provider</param>
|
||||
/// <param name="describer">The error describer</param>
|
||||
public MembersUserStore(IMemberService memberService, UmbracoMapper mapper, IScopeProvider scopeProvider, IdentityErrorDescriber describer)
|
||||
public MemberUserStore(IMemberService memberService, UmbracoMapper mapper, IScopeProvider scopeProvider, IdentityErrorDescriber describer)
|
||||
: base(describer)
|
||||
{
|
||||
_memberService = memberService ?? throw new ArgumentNullException(nameof(memberService));
|
||||
@@ -40,183 +41,213 @@ namespace Umbraco.Cms.Core.Security
|
||||
_scopeProvider = scopeProvider ?? throw new ArgumentNullException(nameof(scopeProvider));
|
||||
}
|
||||
|
||||
//TODO: why is this not supported?
|
||||
/// <summary>
|
||||
/// Not supported in Umbraco
|
||||
/// </summary>
|
||||
/// <inheritdoc />
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public override IQueryable<MembersIdentityUser> Users => throw new NotImplementedException();
|
||||
public override IQueryable<MemberIdentityUser> Users => throw new NotImplementedException();
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Task<string> GetNormalizedUserNameAsync(MembersIdentityUser user, CancellationToken cancellationToken) => GetUserNameAsync(user, cancellationToken);
|
||||
public override Task<string> GetNormalizedUserNameAsync(MemberIdentityUser user, CancellationToken cancellationToken = default) => GetUserNameAsync(user, cancellationToken);
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Task SetNormalizedUserNameAsync(MembersIdentityUser user, string normalizedName, CancellationToken cancellationToken) => SetUserNameAsync(user, normalizedName, cancellationToken);
|
||||
public override Task SetNormalizedUserNameAsync(MemberIdentityUser user, string normalizedName, CancellationToken cancellationToken = default) => SetUserNameAsync(user, normalizedName, cancellationToken);
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Task<IdentityResult> CreateAsync(MembersIdentityUser user, CancellationToken cancellationToken = default)
|
||||
public override Task<IdentityResult> CreateAsync(MemberIdentityUser user, CancellationToken cancellationToken = default)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
ThrowIfDisposed();
|
||||
if (user == null)
|
||||
try
|
||||
{
|
||||
throw new ArgumentNullException(nameof(user));
|
||||
}
|
||||
|
||||
// create member
|
||||
IMember memberEntity = _memberService.CreateMember(
|
||||
user.UserName,
|
||||
user.Email,
|
||||
user.Name.IsNullOrWhiteSpace() ? user.UserName : user.Name,
|
||||
user.MemberTypeAlias.IsNullOrWhiteSpace() ? Constants.Security.DefaultMemberTypeAlias : user.MemberTypeAlias);
|
||||
|
||||
UpdateMemberProperties(memberEntity, user);
|
||||
|
||||
// create the member
|
||||
_memberService.Save(memberEntity);
|
||||
|
||||
if (!memberEntity.HasIdentity)
|
||||
{
|
||||
throw new DataException("Could not create the member, check logs for details");
|
||||
}
|
||||
|
||||
// re-assign id
|
||||
user.Id = UserIdToString(memberEntity.Id);
|
||||
|
||||
// [from backofficeuser] we have to remember whether Logins property is dirty, since the UpdateMemberProperties will reset it.
|
||||
// var isLoginsPropertyDirty = user.IsPropertyDirty(nameof(MembersIdentityUser.Logins));
|
||||
// TODO: confirm re externallogins implementation
|
||||
//if (isLoginsPropertyDirty)
|
||||
//{
|
||||
// _externalLoginService.Save(
|
||||
// user.Id,
|
||||
// user.Logins.Select(x => new ExternalLogin(
|
||||
// x.LoginProvider,
|
||||
// x.ProviderKey,
|
||||
// x.UserData)));
|
||||
//}
|
||||
|
||||
return Task.FromResult(IdentityResult.Success);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Task<IdentityResult> UpdateAsync(MembersIdentityUser user, CancellationToken cancellationToken = default)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
ThrowIfDisposed();
|
||||
if (user == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(user));
|
||||
}
|
||||
|
||||
Attempt<int> asInt = user.Id.TryConvertTo<int>();
|
||||
if (asInt == false)
|
||||
{
|
||||
throw new InvalidOperationException("The user id must be an integer to work with the Umbraco");
|
||||
}
|
||||
|
||||
using (IScope scope = _scopeProvider.CreateScope())
|
||||
{
|
||||
IMember found = _memberService.GetById(asInt.Result);
|
||||
if (found != null)
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
ThrowIfDisposed();
|
||||
if (user == null)
|
||||
{
|
||||
// we have to remember whether Logins property is dirty, since the UpdateMemberProperties will reset it.
|
||||
var isLoginsPropertyDirty = user.IsPropertyDirty(nameof(MembersIdentityUser.Logins));
|
||||
|
||||
if (UpdateMemberProperties(found, user))
|
||||
{
|
||||
_memberService.Save(found);
|
||||
}
|
||||
|
||||
// TODO: when to implement external login service?
|
||||
|
||||
//if (isLoginsPropertyDirty)
|
||||
//{
|
||||
// _externalLoginService.Save(
|
||||
// found.Id,
|
||||
// user.Logins.Select(x => new ExternalLogin(
|
||||
// x.LoginProvider,
|
||||
// x.ProviderKey,
|
||||
// x.UserData)));
|
||||
//}
|
||||
throw new ArgumentNullException(nameof(user));
|
||||
}
|
||||
|
||||
scope.Complete();
|
||||
}
|
||||
// create member
|
||||
IMember memberEntity = _memberService.CreateMember(
|
||||
user.UserName,
|
||||
user.Email,
|
||||
user.Name.IsNullOrWhiteSpace() ? user.UserName : user.Name,
|
||||
user.MemberTypeAlias.IsNullOrWhiteSpace() ? Constants.Security.DefaultMemberTypeAlias : user.MemberTypeAlias);
|
||||
|
||||
return Task.FromResult(IdentityResult.Success);
|
||||
UpdateMemberProperties(memberEntity, user);
|
||||
|
||||
// create the member
|
||||
_memberService.Save(memberEntity);
|
||||
|
||||
if (!memberEntity.HasIdentity)
|
||||
{
|
||||
throw new DataException("Could not create the member, check logs for details");
|
||||
}
|
||||
|
||||
// re-assign id
|
||||
user.Id = UserIdToString(memberEntity.Id);
|
||||
|
||||
// [from backofficeuser] we have to remember whether Logins property is dirty, since the UpdateMemberProperties will reset it.
|
||||
// var isLoginsPropertyDirty = user.IsPropertyDirty(nameof(MembersIdentityUser.Logins));
|
||||
// TODO: confirm re externallogins implementation
|
||||
//if (isLoginsPropertyDirty)
|
||||
//{
|
||||
// _externalLoginService.Save(
|
||||
// user.Id,
|
||||
// user.Logins.Select(x => new ExternalLogin(
|
||||
// x.LoginProvider,
|
||||
// x.ProviderKey,
|
||||
// x.UserData)));
|
||||
//}
|
||||
|
||||
|
||||
return Task.FromResult(IdentityResult.Success);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return Task.FromResult(IdentityResult.Failed(new IdentityError { Code = genericIdentityErrorCode, Description = ex.Message }));
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Task<IdentityResult> DeleteAsync(MembersIdentityUser user, CancellationToken cancellationToken = default)
|
||||
public override Task<IdentityResult> UpdateAsync(MemberIdentityUser user, CancellationToken cancellationToken = default)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
ThrowIfDisposed();
|
||||
if (user == null)
|
||||
try
|
||||
{
|
||||
throw new ArgumentNullException(nameof(user));
|
||||
}
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
ThrowIfDisposed();
|
||||
if (user == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(user));
|
||||
}
|
||||
|
||||
IMember found = _memberService.GetById(UserIdToInt(user.Id));
|
||||
if (found != null)
|
||||
Attempt<int> asInt = user.Id.TryConvertTo<int>();
|
||||
if (asInt == false)
|
||||
{
|
||||
//TODO: should this be thrown, or an identity result?
|
||||
throw new InvalidOperationException("The user id must be an integer to work with Umbraco");
|
||||
}
|
||||
|
||||
using (IScope scope = _scopeProvider.CreateScope())
|
||||
{
|
||||
IMember found = _memberService.GetById(asInt.Result);
|
||||
if (found != null)
|
||||
{
|
||||
// we have to remember whether Logins property is dirty, since the UpdateMemberProperties will reset it.
|
||||
var isLoginsPropertyDirty = user.IsPropertyDirty(nameof(MemberIdentityUser.Logins));
|
||||
|
||||
if (UpdateMemberProperties(found, user))
|
||||
{
|
||||
_memberService.Save(found);
|
||||
}
|
||||
|
||||
// TODO: when to implement external login service?
|
||||
|
||||
//if (isLoginsPropertyDirty)
|
||||
//{
|
||||
// _externalLoginService.Save(
|
||||
// found.Id,
|
||||
// user.Logins.Select(x => new ExternalLogin(
|
||||
// x.LoginProvider,
|
||||
// x.ProviderKey,
|
||||
// x.UserData)));
|
||||
//}
|
||||
}
|
||||
|
||||
scope.Complete();
|
||||
|
||||
return Task.FromResult(IdentityResult.Success);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_memberService.Delete(found);
|
||||
return Task.FromResult(IdentityResult.Failed(new IdentityError { Code = genericIdentityErrorCode, Description = ex.Message }));
|
||||
}
|
||||
|
||||
// TODO: when to implement external login service?
|
||||
//_externalLoginService.DeleteUserLogins(UserIdToInt(user.Id));
|
||||
|
||||
return Task.FromResult(IdentityResult.Success);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Task<MembersIdentityUser> FindByIdAsync(string userId, CancellationToken cancellationToken = default) => FindUserAsync(userId, cancellationToken);
|
||||
public override Task<IdentityResult> DeleteAsync(MemberIdentityUser user, CancellationToken cancellationToken = default)
|
||||
{
|
||||
try
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
ThrowIfDisposed();
|
||||
if (user == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(user));
|
||||
}
|
||||
|
||||
IMember found = _memberService.GetById(UserIdToInt(user.Id));
|
||||
if (found != null)
|
||||
{
|
||||
_memberService.Delete(found);
|
||||
}
|
||||
|
||||
// TODO: when to implement external login service?
|
||||
//_externalLoginService.DeleteUserLogins(UserIdToInt(user.Id));
|
||||
|
||||
return Task.FromResult(IdentityResult.Success);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return Task.FromResult(IdentityResult.Failed(new IdentityError { Code = genericIdentityErrorCode, Description = ex.Message }));
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override Task<MembersIdentityUser> FindUserAsync(string userId, CancellationToken cancellationToken)
|
||||
public override Task<MemberIdentityUser> FindByIdAsync(string userId, CancellationToken cancellationToken = default) => FindUserAsync(userId, cancellationToken);
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override Task<MemberIdentityUser> FindUserAsync(string userId, CancellationToken cancellationToken)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
ThrowIfDisposed();
|
||||
|
||||
if (string.IsNullOrWhiteSpace(userId))
|
||||
{
|
||||
throw new ArgumentNullException(nameof(userId));
|
||||
}
|
||||
|
||||
IMember user = _memberService.GetById(UserIdToInt(userId));
|
||||
if (user == null)
|
||||
{
|
||||
return Task.FromResult((MembersIdentityUser)null);
|
||||
return Task.FromResult((MemberIdentityUser)null);
|
||||
}
|
||||
|
||||
return Task.FromResult(AssignLoginsCallback(_mapper.Map<MembersIdentityUser>(user)));
|
||||
return Task.FromResult(AssignLoginsCallback(_mapper.Map<MemberIdentityUser>(user)));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Task<MembersIdentityUser> FindByNameAsync(string userName, CancellationToken cancellationToken = default)
|
||||
public override Task<MemberIdentityUser> FindByNameAsync(string userName, CancellationToken cancellationToken = default)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
ThrowIfDisposed();
|
||||
IMember user = _memberService.GetByUsername(userName);
|
||||
if (user == null)
|
||||
{
|
||||
return Task.FromResult((MembersIdentityUser)null);
|
||||
return Task.FromResult((MemberIdentityUser)null);
|
||||
}
|
||||
|
||||
MembersIdentityUser result = AssignLoginsCallback(_mapper.Map<MembersIdentityUser>(user));
|
||||
MemberIdentityUser result = AssignLoginsCallback(_mapper.Map<MemberIdentityUser>(user));
|
||||
|
||||
return Task.FromResult(result);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override async Task SetPasswordHashAsync(MembersIdentityUser user, string passwordHash, CancellationToken cancellationToken = default)
|
||||
public override async Task SetPasswordHashAsync(MemberIdentityUser user, string passwordHash, CancellationToken cancellationToken = default)
|
||||
{
|
||||
await base.SetPasswordHashAsync(user, passwordHash, cancellationToken);
|
||||
|
||||
user.PasswordConfig = null; // Clear this so that it's reset at the repository level
|
||||
// Clear this so that it's reset at the repository level
|
||||
user.PasswordConfig = null;
|
||||
user.LastPasswordChangeDateUtc = DateTime.UtcNow;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override async Task<bool> HasPasswordAsync(MembersIdentityUser user, CancellationToken cancellationToken = default)
|
||||
public override async Task<bool> HasPasswordAsync(MemberIdentityUser user, CancellationToken cancellationToken = default)
|
||||
{
|
||||
// This checks if it's null
|
||||
var result = await base.HasPasswordAsync(user, cancellationToken);
|
||||
bool result = await base.HasPasswordAsync(user, cancellationToken);
|
||||
if (result)
|
||||
{
|
||||
// we also want to check empty
|
||||
@@ -227,28 +258,28 @@ namespace Umbraco.Cms.Core.Security
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Task<MembersIdentityUser> FindByEmailAsync(string email, CancellationToken cancellationToken = default)
|
||||
public override Task<MemberIdentityUser> FindByEmailAsync(string email, CancellationToken cancellationToken = default)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
ThrowIfDisposed();
|
||||
IMember member = _memberService.GetByEmail(email);
|
||||
MembersIdentityUser result = member == null
|
||||
MemberIdentityUser result = member == null
|
||||
? null
|
||||
: _mapper.Map<MembersIdentityUser>(member);
|
||||
: _mapper.Map<MemberIdentityUser>(member);
|
||||
|
||||
return Task.FromResult(AssignLoginsCallback(result));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Task<string> GetNormalizedEmailAsync(MembersIdentityUser user, CancellationToken cancellationToken)
|
||||
public override Task<string> GetNormalizedEmailAsync(MemberIdentityUser user, CancellationToken cancellationToken)
|
||||
=> GetEmailAsync(user, cancellationToken);
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Task SetNormalizedEmailAsync(MembersIdentityUser user, string normalizedEmail, CancellationToken cancellationToken)
|
||||
public override Task SetNormalizedEmailAsync(MemberIdentityUser user, string normalizedEmail, CancellationToken cancellationToken)
|
||||
=> SetEmailAsync(user, normalizedEmail, cancellationToken);
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Task AddLoginAsync(MembersIdentityUser user, UserLoginInfo login, CancellationToken cancellationToken = default)
|
||||
public override Task AddLoginAsync(MemberIdentityUser user, UserLoginInfo login, CancellationToken cancellationToken = default)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
ThrowIfDisposed();
|
||||
@@ -262,8 +293,22 @@ namespace Umbraco.Cms.Core.Security
|
||||
throw new ArgumentNullException(nameof(login));
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(login.LoginProvider))
|
||||
{
|
||||
throw new ArgumentNullException(nameof(login.LoginProvider));
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(login.ProviderKey))
|
||||
{
|
||||
throw new ArgumentNullException(nameof(login.ProviderKey));
|
||||
}
|
||||
|
||||
ICollection<IIdentityUserLogin> logins = user.Logins;
|
||||
var instance = new IdentityUserLogin(login.LoginProvider, login.ProviderKey, user.Id.ToString());
|
||||
var instance = new IdentityUserLogin(
|
||||
login.LoginProvider,
|
||||
login.ProviderKey,
|
||||
user.Id.ToString());
|
||||
|
||||
IdentityUserLogin userLogin = instance;
|
||||
logins.Add(userLogin);
|
||||
|
||||
@@ -271,7 +316,7 @@ namespace Umbraco.Cms.Core.Security
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Task RemoveLoginAsync(MembersIdentityUser user, string loginProvider, string providerKey, CancellationToken cancellationToken = default)
|
||||
public override Task RemoveLoginAsync(MemberIdentityUser user, string loginProvider, string providerKey, CancellationToken cancellationToken = default)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
ThrowIfDisposed();
|
||||
@@ -280,6 +325,16 @@ namespace Umbraco.Cms.Core.Security
|
||||
throw new ArgumentNullException(nameof(user));
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(loginProvider))
|
||||
{
|
||||
throw new ArgumentNullException(nameof(loginProvider));
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(providerKey))
|
||||
{
|
||||
throw new ArgumentNullException(nameof(providerKey));
|
||||
}
|
||||
|
||||
IIdentityUserLogin userLogin = user.Logins.SingleOrDefault(l => l.LoginProvider == loginProvider && l.ProviderKey == providerKey);
|
||||
if (userLogin != null)
|
||||
{
|
||||
@@ -290,7 +345,7 @@ namespace Umbraco.Cms.Core.Security
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Task<IList<UserLoginInfo>> GetLoginsAsync(MembersIdentityUser user, CancellationToken cancellationToken = default)
|
||||
public override Task<IList<UserLoginInfo>> GetLoginsAsync(MemberIdentityUser user, CancellationToken cancellationToken = default)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
ThrowIfDisposed();
|
||||
@@ -308,24 +363,35 @@ namespace Umbraco.Cms.Core.Security
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
ThrowIfDisposed();
|
||||
|
||||
MembersIdentityUser user = await FindUserAsync(userId, cancellationToken);
|
||||
if (string.IsNullOrWhiteSpace(loginProvider))
|
||||
{
|
||||
throw new ArgumentNullException(nameof(loginProvider));
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(providerKey))
|
||||
{
|
||||
throw new ArgumentNullException(nameof(providerKey));
|
||||
}
|
||||
|
||||
MemberIdentityUser user = await FindUserAsync(userId, cancellationToken);
|
||||
if (user == null)
|
||||
{
|
||||
return null;
|
||||
return await Task.FromResult((IdentityUserLogin<string>)null);
|
||||
}
|
||||
|
||||
IList<UserLoginInfo> logins = await GetLoginsAsync(user, cancellationToken);
|
||||
UserLoginInfo found = logins.FirstOrDefault(x => x.ProviderKey == providerKey && x.LoginProvider == loginProvider);
|
||||
if (found == null)
|
||||
{
|
||||
return null;
|
||||
return await Task.FromResult((IdentityUserLogin<string>)null);
|
||||
}
|
||||
|
||||
return new IdentityUserLogin<string>
|
||||
{
|
||||
LoginProvider = found.LoginProvider,
|
||||
ProviderKey = found.ProviderKey,
|
||||
ProviderDisplayName = found.ProviderDisplayName, // TODO: We don't store this value so it will be null
|
||||
// TODO: We don't store this value so it will be null
|
||||
ProviderDisplayName = found.ProviderDisplayName,
|
||||
UserId = user.Id
|
||||
};
|
||||
}
|
||||
@@ -336,9 +402,19 @@ namespace Umbraco.Cms.Core.Security
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
ThrowIfDisposed();
|
||||
|
||||
if (string.IsNullOrWhiteSpace(loginProvider))
|
||||
{
|
||||
throw new ArgumentNullException(nameof(loginProvider));
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(providerKey))
|
||||
{
|
||||
throw new ArgumentNullException(nameof(providerKey));
|
||||
}
|
||||
|
||||
var logins = new List<IIdentityUserLogin>();
|
||||
|
||||
// TODO: external login needed?
|
||||
// TODO: external login needed
|
||||
//_externalLoginService.Find(loginProvider, providerKey).ToList();
|
||||
if (logins.Count == 0)
|
||||
{
|
||||
@@ -350,15 +426,20 @@ namespace Umbraco.Cms.Core.Security
|
||||
{
|
||||
LoginProvider = found.LoginProvider,
|
||||
ProviderKey = found.ProviderKey,
|
||||
ProviderDisplayName = null, // TODO: We don't store this value so it will be null
|
||||
// TODO: We don't store this value so it will be null
|
||||
ProviderDisplayName = null,
|
||||
UserId = found.UserId
|
||||
});
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Task AddToRoleAsync(MembersIdentityUser user, string role, CancellationToken cancellationToken = default)
|
||||
public override Task AddToRoleAsync(MemberIdentityUser user, string role, CancellationToken cancellationToken = default)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
if (cancellationToken != null)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
}
|
||||
|
||||
ThrowIfDisposed();
|
||||
if (user == null)
|
||||
{
|
||||
@@ -387,7 +468,7 @@ namespace Umbraco.Cms.Core.Security
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override Task RemoveFromRoleAsync(MembersIdentityUser user, string role, CancellationToken cancellationToken = default)
|
||||
public override Task RemoveFromRoleAsync(MemberIdentityUser user, string role, CancellationToken cancellationToken = default)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
@@ -420,7 +501,7 @@ namespace Umbraco.Cms.Core.Security
|
||||
/// <summary>
|
||||
/// Gets a list of role names the specified user belongs to.
|
||||
/// </summary>
|
||||
public override Task<IList<string>> GetRolesAsync(MembersIdentityUser user, CancellationToken cancellationToken = default)
|
||||
public override Task<IList<string>> GetRolesAsync(MemberIdentityUser user, CancellationToken cancellationToken = default)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
ThrowIfDisposed();
|
||||
@@ -443,7 +524,7 @@ namespace Umbraco.Cms.Core.Security
|
||||
/// <summary>
|
||||
/// Returns true if a user is in the role
|
||||
/// </summary>
|
||||
public override Task<bool> IsInRoleAsync(MembersIdentityUser user, string normalizedRoleName, CancellationToken cancellationToken = default)
|
||||
public override Task<bool> IsInRoleAsync(MemberIdentityUser user, string roleName, CancellationToken cancellationToken = default)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
ThrowIfDisposed();
|
||||
@@ -452,48 +533,59 @@ namespace Umbraco.Cms.Core.Security
|
||||
throw new ArgumentNullException(nameof(user));
|
||||
}
|
||||
|
||||
return Task.FromResult(user.Roles.Select(x => x.RoleId).InvariantContains(normalizedRoleName));
|
||||
if (string.IsNullOrWhiteSpace(roleName))
|
||||
{
|
||||
throw new ArgumentNullException(nameof(roleName));
|
||||
}
|
||||
|
||||
return Task.FromResult(user.Roles.Select(x => x.RoleId).InvariantContains(roleName));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Lists all users of a given role.
|
||||
/// </summary>
|
||||
public override Task<IList<MembersIdentityUser>> GetUsersInRoleAsync(string normalizedRoleName, CancellationToken cancellationToken = default)
|
||||
public override Task<IList<MemberIdentityUser>> GetUsersInRoleAsync(string roleName, CancellationToken cancellationToken = default)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
ThrowIfDisposed();
|
||||
if (normalizedRoleName == null)
|
||||
|
||||
if (string.IsNullOrWhiteSpace(roleName))
|
||||
{
|
||||
throw new ArgumentNullException(nameof(normalizedRoleName));
|
||||
throw new ArgumentNullException(nameof(roleName));
|
||||
}
|
||||
|
||||
IEnumerable<IMember> members = _memberService.GetMembersByMemberType(normalizedRoleName);
|
||||
IEnumerable<IMember> members = _memberService.GetMembersByMemberType(roleName);
|
||||
|
||||
IList<MembersIdentityUser> membersIdentityUsers = members.Select(x => _mapper.Map<MembersIdentityUser>(x)).ToList();
|
||||
IList<MemberIdentityUser> membersIdentityUsers = members.Select(x => _mapper.Map<MemberIdentityUser>(x)).ToList();
|
||||
|
||||
return Task.FromResult(membersIdentityUsers);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override Task<IdentityRole<string>> FindRoleAsync(string normalizedRoleName, CancellationToken cancellationToken)
|
||||
protected override Task<UmbracoIdentityRole> FindRoleAsync(string roleName, CancellationToken cancellationToken)
|
||||
{
|
||||
IMemberGroup group = _memberService.GetAllRoles().SingleOrDefault(x => x.Name == normalizedRoleName);
|
||||
if (group == null)
|
||||
if (string.IsNullOrWhiteSpace(roleName))
|
||||
{
|
||||
return Task.FromResult((IdentityRole<string>)null);
|
||||
throw new ArgumentNullException(nameof(roleName));
|
||||
}
|
||||
|
||||
return Task.FromResult(new IdentityRole<string>(group.Name)
|
||||
IMemberGroup group = _memberService.GetAllRoles().SingleOrDefault(x => x.Name == roleName);
|
||||
if (group == null)
|
||||
{
|
||||
return Task.FromResult((UmbracoIdentityRole)null);
|
||||
}
|
||||
|
||||
return Task.FromResult(new UmbracoIdentityRole(group.Name)
|
||||
{
|
||||
//TODO: what should the alias be?
|
||||
Id = @group.Id.ToString()
|
||||
Id = group.Id.ToString()
|
||||
});
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override async Task<IdentityUserRole<string>> FindUserRoleAsync(string userId, string roleId, CancellationToken cancellationToken)
|
||||
{
|
||||
MembersIdentityUser user = await FindUserAsync(userId, cancellationToken);
|
||||
MemberIdentityUser user = await FindUserAsync(userId, cancellationToken);
|
||||
if (user == null)
|
||||
{
|
||||
return null;
|
||||
@@ -504,7 +596,7 @@ namespace Umbraco.Cms.Core.Security
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Task<string> GetSecurityStampAsync(MembersIdentityUser user, CancellationToken cancellationToken = default)
|
||||
public override Task<string> GetSecurityStampAsync(MemberIdentityUser user, CancellationToken cancellationToken = default)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
ThrowIfDisposed();
|
||||
@@ -519,23 +611,23 @@ namespace Umbraco.Cms.Core.Security
|
||||
: user.SecurityStamp);
|
||||
}
|
||||
|
||||
private MembersIdentityUser AssignLoginsCallback(MembersIdentityUser user)
|
||||
private MemberIdentityUser AssignLoginsCallback(MemberIdentityUser user)
|
||||
{
|
||||
if (user != null)
|
||||
{
|
||||
//TODO: when to
|
||||
//TODO: implement
|
||||
//user.SetLoginsCallback(new Lazy<IEnumerable<IIdentityUserLogin>>(() => _externalLoginService.GetAll(UserIdToInt(user.Id))));
|
||||
}
|
||||
|
||||
return user;
|
||||
}
|
||||
|
||||
private bool UpdateMemberProperties(IMember member, MembersIdentityUser identityUserMember)
|
||||
private bool UpdateMemberProperties(IMember member, MemberIdentityUser identityUserMember)
|
||||
{
|
||||
var anythingChanged = false;
|
||||
|
||||
// don't assign anything if nothing has changed as this will trigger the track changes of the model
|
||||
if (identityUserMember.IsPropertyDirty(nameof(MembersIdentityUser.LastLoginDateUtc))
|
||||
if (identityUserMember.IsPropertyDirty(nameof(MemberIdentityUser.LastLoginDateUtc))
|
||||
|| (member.LastLoginDate != default && identityUserMember.LastLoginDateUtc.HasValue == false)
|
||||
|| (identityUserMember.LastLoginDateUtc.HasValue && member.LastLoginDate.ToUniversalTime() != identityUserMember.LastLoginDateUtc.Value))
|
||||
{
|
||||
@@ -546,7 +638,7 @@ namespace Umbraco.Cms.Core.Security
|
||||
member.LastLoginDate = dt;
|
||||
}
|
||||
|
||||
if (identityUserMember.IsPropertyDirty(nameof(MembersIdentityUser.LastPasswordChangeDateUtc))
|
||||
if (identityUserMember.IsPropertyDirty(nameof(MemberIdentityUser.LastPasswordChangeDateUtc))
|
||||
|| (member.LastPasswordChangeDate != default && identityUserMember.LastPasswordChangeDateUtc.HasValue == false)
|
||||
|| (identityUserMember.LastPasswordChangeDateUtc.HasValue && member.LastPasswordChangeDate.ToUniversalTime() != identityUserMember.LastPasswordChangeDateUtc.Value))
|
||||
{
|
||||
@@ -554,7 +646,7 @@ namespace Umbraco.Cms.Core.Security
|
||||
member.LastPasswordChangeDate = identityUserMember.LastPasswordChangeDateUtc.Value.ToLocalTime();
|
||||
}
|
||||
|
||||
if (identityUserMember.IsPropertyDirty(nameof(MembersIdentityUser.EmailConfirmed))
|
||||
if (identityUserMember.IsPropertyDirty(nameof(MemberIdentityUser.EmailConfirmed))
|
||||
|| (member.EmailConfirmedDate.HasValue && member.EmailConfirmedDate.Value != default && identityUserMember.EmailConfirmed == false)
|
||||
|| ((member.EmailConfirmedDate.HasValue == false || member.EmailConfirmedDate.Value == default) && identityUserMember.EmailConfirmed))
|
||||
{
|
||||
@@ -562,21 +654,21 @@ namespace Umbraco.Cms.Core.Security
|
||||
member.EmailConfirmedDate = identityUserMember.EmailConfirmed ? (DateTime?)DateTime.Now : null;
|
||||
}
|
||||
|
||||
if (identityUserMember.IsPropertyDirty(nameof(MembersIdentityUser.Name))
|
||||
if (identityUserMember.IsPropertyDirty(nameof(MemberIdentityUser.Name))
|
||||
&& member.Name != identityUserMember.Name && identityUserMember.Name.IsNullOrWhiteSpace() == false)
|
||||
{
|
||||
anythingChanged = true;
|
||||
member.Name = identityUserMember.Name;
|
||||
}
|
||||
|
||||
if (identityUserMember.IsPropertyDirty(nameof(MembersIdentityUser.Email))
|
||||
if (identityUserMember.IsPropertyDirty(nameof(MemberIdentityUser.Email))
|
||||
&& member.Email != identityUserMember.Email && identityUserMember.Email.IsNullOrWhiteSpace() == false)
|
||||
{
|
||||
anythingChanged = true;
|
||||
member.Email = identityUserMember.Email;
|
||||
}
|
||||
|
||||
if (identityUserMember.IsPropertyDirty(nameof(MembersIdentityUser.AccessFailedCount))
|
||||
if (identityUserMember.IsPropertyDirty(nameof(MemberIdentityUser.AccessFailedCount))
|
||||
&& member.FailedPasswordAttempts != identityUserMember.AccessFailedCount)
|
||||
{
|
||||
anythingChanged = true;
|
||||
@@ -601,14 +693,14 @@ namespace Umbraco.Cms.Core.Security
|
||||
member.IsApproved = identityUserMember.IsApproved;
|
||||
}
|
||||
|
||||
if (identityUserMember.IsPropertyDirty(nameof(MembersIdentityUser.UserName))
|
||||
if (identityUserMember.IsPropertyDirty(nameof(MemberIdentityUser.UserName))
|
||||
&& member.Username != identityUserMember.UserName && identityUserMember.UserName.IsNullOrWhiteSpace() == false)
|
||||
{
|
||||
anythingChanged = true;
|
||||
member.Username = identityUserMember.UserName;
|
||||
}
|
||||
|
||||
if (identityUserMember.IsPropertyDirty(nameof(MembersIdentityUser.PasswordHash))
|
||||
if (identityUserMember.IsPropertyDirty(nameof(MemberIdentityUser.PasswordHash))
|
||||
&& member.RawPasswordValue != identityUserMember.PasswordHash && identityUserMember.PasswordHash.IsNullOrWhiteSpace() == false)
|
||||
{
|
||||
anythingChanged = true;
|
||||
@@ -623,7 +715,7 @@ namespace Umbraco.Cms.Core.Security
|
||||
}
|
||||
|
||||
// TODO: Fix this for Groups too (as per backoffice comment)
|
||||
if (identityUserMember.IsPropertyDirty(nameof(MembersIdentityUser.Roles)) || identityUserMember.IsPropertyDirty(nameof(MembersIdentityUser.Groups)))
|
||||
if (identityUserMember.IsPropertyDirty(nameof(MemberIdentityUser.Roles)) || identityUserMember.IsPropertyDirty(nameof(MemberIdentityUser.Groups)))
|
||||
{
|
||||
}
|
||||
|
||||
@@ -651,42 +743,42 @@ namespace Umbraco.Cms.Core.Security
|
||||
/// </summary>
|
||||
/// <inheritdoc />
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public override Task<IList<Claim>> GetClaimsAsync(MembersIdentityUser user, CancellationToken cancellationToken = default) => throw new NotImplementedException();
|
||||
public override Task<IList<Claim>> GetClaimsAsync(MemberIdentityUser user, CancellationToken cancellationToken = default) => throw new NotImplementedException();
|
||||
|
||||
/// <summary>
|
||||
/// Not supported in Umbraco
|
||||
/// </summary>
|
||||
/// <inheritdoc />
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public override Task AddClaimsAsync(MembersIdentityUser user, IEnumerable<Claim> claims, CancellationToken cancellationToken = default) => throw new NotImplementedException();
|
||||
public override Task AddClaimsAsync(MemberIdentityUser user, IEnumerable<Claim> claims, CancellationToken cancellationToken = default) => throw new NotImplementedException();
|
||||
|
||||
/// <summary>
|
||||
/// Not supported in Umbraco
|
||||
/// </summary>
|
||||
/// <inheritdoc />
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public override Task ReplaceClaimAsync(MembersIdentityUser user, Claim claim, Claim newClaim, CancellationToken cancellationToken = default) => throw new NotImplementedException();
|
||||
public override Task ReplaceClaimAsync(MemberIdentityUser user, Claim claim, Claim newClaim, CancellationToken cancellationToken = default) => throw new NotImplementedException();
|
||||
|
||||
/// <summary>
|
||||
/// Not supported in Umbraco
|
||||
/// </summary>
|
||||
/// <inheritdoc />
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public override Task RemoveClaimsAsync(MembersIdentityUser user, IEnumerable<Claim> claims, CancellationToken cancellationToken = default) => throw new NotImplementedException();
|
||||
public override Task RemoveClaimsAsync(MemberIdentityUser user, IEnumerable<Claim> claims, CancellationToken cancellationToken = default) => throw new NotImplementedException();
|
||||
|
||||
/// <summary>
|
||||
/// Not supported in Umbraco
|
||||
/// </summary>
|
||||
/// <inheritdoc />
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public override Task<IList<MembersIdentityUser>> GetUsersForClaimAsync(Claim claim, CancellationToken cancellationToken = default) => throw new NotImplementedException();
|
||||
public override Task<IList<MemberIdentityUser>> GetUsersForClaimAsync(Claim claim, CancellationToken cancellationToken = default) => throw new NotImplementedException();
|
||||
|
||||
/// <summary>
|
||||
/// Not supported in Umbraco
|
||||
/// </summary>
|
||||
/// <inheritdoc />
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
protected override Task<IdentityUserToken<string>> FindTokenAsync(MembersIdentityUser user, string loginProvider, string name, CancellationToken cancellationToken) => throw new NotImplementedException();
|
||||
protected override Task<IdentityUserToken<string>> FindTokenAsync(MemberIdentityUser user, string loginProvider, string name, CancellationToken cancellationToken) => throw new NotImplementedException();
|
||||
|
||||
/// <summary>
|
||||
/// Not supported in Umbraco
|
||||
105
src/Umbraco.Infrastructure/Security/UmbracoIdentityRole.cs
Normal file
105
src/Umbraco.Infrastructure/Security/UmbracoIdentityRole.cs
Normal file
@@ -0,0 +1,105 @@
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Umbraco.Cms.Core.Models.Entities;
|
||||
|
||||
namespace Umbraco.Cms.Core.Models.Identity
|
||||
{
|
||||
public class UmbracoIdentityRole : IdentityRole, IRememberBeingDirty
|
||||
{
|
||||
private string _id;
|
||||
private string _name;
|
||||
|
||||
public UmbracoIdentityRole(string roleName) : base(roleName)
|
||||
{
|
||||
}
|
||||
|
||||
public UmbracoIdentityRole()
|
||||
{
|
||||
}
|
||||
|
||||
public event PropertyChangedEventHandler PropertyChanged
|
||||
{
|
||||
add
|
||||
{
|
||||
BeingDirty.PropertyChanged += value;
|
||||
}
|
||||
|
||||
remove
|
||||
{
|
||||
BeingDirty.PropertyChanged -= value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Id
|
||||
{
|
||||
get => _id;
|
||||
set
|
||||
{
|
||||
_id = value;
|
||||
HasIdentity = true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Name
|
||||
{
|
||||
get => _name;
|
||||
set => BeingDirty.SetPropertyValueAndDetectChanges(value, ref _name, nameof(Name));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string NormalizedName { get => base.Name; set => base.Name = value; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether returns an Id has been set on this object this will be false if the object is new and not persisted to the database
|
||||
/// </summary>
|
||||
public bool HasIdentity { get; protected set; }
|
||||
|
||||
// TODO: We should support this and it's logic
|
||||
public override string ConcurrencyStamp { get => base.ConcurrencyStamp; set => base.ConcurrencyStamp = value; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="BeingDirty"/> for change tracking
|
||||
/// </summary>
|
||||
protected BeingDirty BeingDirty { get; } = new BeingDirty();
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool IsDirty() => BeingDirty.IsDirty();
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool IsPropertyDirty(string propName) => BeingDirty.IsPropertyDirty(propName);
|
||||
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<string> GetDirtyProperties() => BeingDirty.GetDirtyProperties();
|
||||
|
||||
/// <inheritdoc />
|
||||
public void ResetDirtyProperties() => BeingDirty.ResetDirtyProperties();
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool WasDirty() => BeingDirty.WasDirty();
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool WasPropertyDirty(string propertyName) => BeingDirty.WasPropertyDirty(propertyName);
|
||||
|
||||
/// <inheritdoc />
|
||||
public void ResetWereDirtyProperties() => BeingDirty.ResetWereDirtyProperties();
|
||||
|
||||
/// <inheritdoc />
|
||||
public void ResetDirtyProperties(bool rememberDirty) => BeingDirty.ResetDirtyProperties(rememberDirty);
|
||||
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<string> GetWereDirtyProperties() => BeingDirty.GetWereDirtyProperties();
|
||||
|
||||
/// <summary>
|
||||
/// Disables change tracking.
|
||||
/// </summary>
|
||||
public void DisableChangeTracking() => BeingDirty.DisableChangeTracking();
|
||||
|
||||
/// <summary>
|
||||
/// Enables change tracking.
|
||||
/// </summary>
|
||||
public void EnableChangeTracking() => BeingDirty.EnableChangeTracking();
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,7 @@ using Umbraco.Cms.Core.Models.Entities;
|
||||
|
||||
namespace Umbraco.Cms.Core.Models.Identity
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Abstract class for use in Umbraco Identity for users and members
|
||||
/// </summary>
|
||||
|
||||
@@ -68,7 +68,8 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
CreatorId = userId
|
||||
};
|
||||
|
||||
if (scope.Events.DispatchCancelable(SavingContainer, this, new SaveEventArgs<EntityContainer>(container, evtMsgs)))
|
||||
var savingEntityContainerNotification = new EntityContainerSavingNotification(container, evtMsgs);
|
||||
if (scope.Notifications.PublishCancelable(savingEntityContainerNotification))
|
||||
{
|
||||
scope.Complete();
|
||||
return OperationResult.Attempt.Cancel(evtMsgs, container);
|
||||
@@ -77,7 +78,8 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
_dataTypeContainerRepository.Save(container);
|
||||
scope.Complete();
|
||||
|
||||
scope.Events.Dispatch(SavedContainer, this, new SaveEventArgs<EntityContainer>(container, evtMsgs));
|
||||
scope.Notifications.Publish(new EntityContainerSavedNotification(container, evtMsgs).WithStateFrom(savingEntityContainerNotification));
|
||||
|
||||
// TODO: Audit trail ?
|
||||
|
||||
return OperationResult.Attempt.Succeed(evtMsgs, container);
|
||||
@@ -153,7 +155,8 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
|
||||
using (var scope = ScopeProvider.CreateScope())
|
||||
{
|
||||
if (scope.Events.DispatchCancelable(SavingContainer, this, new SaveEventArgs<EntityContainer>(container, evtMsgs)))
|
||||
var savingEntityContainerNotification = new EntityContainerSavingNotification(container, evtMsgs);
|
||||
if (scope.Notifications.PublishCancelable(savingEntityContainerNotification))
|
||||
{
|
||||
scope.Complete();
|
||||
return OperationResult.Attempt.Cancel(evtMsgs);
|
||||
@@ -161,7 +164,7 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
|
||||
_dataTypeContainerRepository.Save(container);
|
||||
|
||||
scope.Events.Dispatch(SavedContainer, this, new SaveEventArgs<EntityContainer>(container, evtMsgs));
|
||||
scope.Notifications.Publish(new EntityContainerSavedNotification(container, evtMsgs).WithStateFrom(savingEntityContainerNotification));
|
||||
scope.Complete();
|
||||
}
|
||||
|
||||
@@ -186,7 +189,8 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
return Attempt.Fail(new OperationResult(OperationResultType.FailedCannot, evtMsgs));
|
||||
}
|
||||
|
||||
if (scope.Events.DispatchCancelable(DeletingContainer, this, new DeleteEventArgs<EntityContainer>(container, evtMsgs)))
|
||||
var deletingEntityContainerNotification = new EntityContainerDeletingNotification(container, evtMsgs);
|
||||
if (scope.Notifications.PublishCancelable(deletingEntityContainerNotification))
|
||||
{
|
||||
scope.Complete();
|
||||
return Attempt.Fail(new OperationResult(OperationResultType.FailedCancelledByEvent, evtMsgs));
|
||||
@@ -194,7 +198,7 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
|
||||
_dataTypeContainerRepository.Delete(container);
|
||||
|
||||
scope.Events.Dispatch(DeletedContainer, this, new DeleteEventArgs<EntityContainer>(container, evtMsgs));
|
||||
scope.Notifications.Publish(new EntityContainerDeletedNotification(container, evtMsgs).WithStateFrom(deletingEntityContainerNotification));
|
||||
scope.Complete();
|
||||
}
|
||||
|
||||
@@ -217,11 +221,17 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
|
||||
container.Name = name;
|
||||
|
||||
var renamingEntityContainerNotification = new EntityContainerRenamingNotification(container, evtMsgs);
|
||||
if (scope.Notifications.PublishCancelable(renamingEntityContainerNotification))
|
||||
{
|
||||
scope.Complete();
|
||||
return OperationResult.Attempt.Cancel(evtMsgs, container);
|
||||
}
|
||||
|
||||
_dataTypeContainerRepository.Save(container);
|
||||
scope.Complete();
|
||||
|
||||
// TODO: triggering SavedContainer with a different name?!
|
||||
scope.Events.Dispatch(SavedContainer, this, new SaveEventArgs<EntityContainer>(container, evtMsgs), "RenamedContainer");
|
||||
scope.Notifications.Publish(new EntityContainerRenamedNotification(container, evtMsgs).WithStateFrom(renamingEntityContainerNotification));
|
||||
|
||||
return OperationResult.Attempt.Succeed(OperationResultType.Success, evtMsgs, container);
|
||||
}
|
||||
@@ -341,8 +351,9 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
using (var scope = ScopeProvider.CreateScope())
|
||||
{
|
||||
var moveEventInfo = new MoveEventInfo<IDataType>(toMove, toMove.Path, parentId);
|
||||
var moveEventArgs = new MoveEventArgs<IDataType>(evtMsgs, moveEventInfo);
|
||||
if (scope.Events.DispatchCancelable(Moving, this, moveEventArgs))
|
||||
|
||||
var movingDataTypeNotification = new DataTypeMovingNotification(moveEventInfo, evtMsgs);
|
||||
if (scope.Notifications.PublishCancelable(movingDataTypeNotification))
|
||||
{
|
||||
scope.Complete();
|
||||
return OperationResult.Attempt.Fail(MoveOperationStatusType.FailedCancelledByEvent, evtMsgs);
|
||||
@@ -359,9 +370,8 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
}
|
||||
moveInfo.AddRange(_dataTypeRepository.Move(toMove, container));
|
||||
|
||||
moveEventArgs.MoveInfoCollection = moveInfo;
|
||||
moveEventArgs.CanCancel = false;
|
||||
scope.Events.Dispatch(Moved, this, moveEventArgs);
|
||||
scope.Notifications.Publish(new DataTypeMovedNotification(moveEventInfo, evtMsgs).WithStateFrom(movingDataTypeNotification));
|
||||
|
||||
scope.Complete();
|
||||
}
|
||||
catch (DataOperationException<MoveOperationStatusType> ex)
|
||||
@@ -381,12 +391,15 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
/// <param name="userId">Id of the user issuing the save</param>
|
||||
public void Save(IDataType dataType, int userId = Cms.Core.Constants.Security.SuperUserId)
|
||||
{
|
||||
var evtMsgs = EventMessagesFactory.Get();
|
||||
dataType.CreatorId = userId;
|
||||
|
||||
using (var scope = ScopeProvider.CreateScope())
|
||||
{
|
||||
var saveEventArgs = new SaveEventArgs<IDataType>(dataType);
|
||||
if (scope.Events.DispatchCancelable(Saving, this, saveEventArgs))
|
||||
|
||||
var savingDataTypeNotification = new DataTypeSavingNotification(dataType, evtMsgs);
|
||||
if (scope.Notifications.PublishCancelable(savingDataTypeNotification))
|
||||
{
|
||||
scope.Complete();
|
||||
return;
|
||||
@@ -404,8 +417,8 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
|
||||
_dataTypeRepository.Save(dataType);
|
||||
|
||||
saveEventArgs.CanCancel = false;
|
||||
scope.Events.Dispatch(Saved, this, saveEventArgs);
|
||||
scope.Notifications.Publish(new DataTypeSavedNotification(dataType, evtMsgs).WithStateFrom(savingDataTypeNotification));
|
||||
|
||||
Audit(AuditType.Save, userId, dataType.Id);
|
||||
scope.Complete();
|
||||
}
|
||||
@@ -429,12 +442,13 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
/// <param name="raiseEvents">Boolean indicating whether or not to raise events</param>
|
||||
public void Save(IEnumerable<IDataType> dataTypeDefinitions, int userId, bool raiseEvents)
|
||||
{
|
||||
var evtMsgs = EventMessagesFactory.Get();
|
||||
var dataTypeDefinitionsA = dataTypeDefinitions.ToArray();
|
||||
var saveEventArgs = new SaveEventArgs<IDataType>(dataTypeDefinitionsA);
|
||||
|
||||
using (var scope = ScopeProvider.CreateScope())
|
||||
{
|
||||
if (raiseEvents && scope.Events.DispatchCancelable(Saving, this, saveEventArgs))
|
||||
var savingDataTypeNotification = new DataTypeSavingNotification(dataTypeDefinitions, evtMsgs);
|
||||
if (raiseEvents && scope.Notifications.PublishCancelable(savingDataTypeNotification))
|
||||
{
|
||||
scope.Complete();
|
||||
return;
|
||||
@@ -448,8 +462,7 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
|
||||
if (raiseEvents)
|
||||
{
|
||||
saveEventArgs.CanCancel = false;
|
||||
scope.Events.Dispatch(Saved, this, saveEventArgs);
|
||||
scope.Notifications.Publish(new DataTypeSavedNotification(dataTypeDefinitions, evtMsgs).WithStateFrom(savingDataTypeNotification));
|
||||
}
|
||||
Audit(AuditType.Save, userId, -1);
|
||||
|
||||
@@ -468,10 +481,11 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
/// <param name="userId">Optional Id of the user issuing the deletion</param>
|
||||
public void Delete(IDataType dataType, int userId = Cms.Core.Constants.Security.SuperUserId)
|
||||
{
|
||||
var evtMsgs = EventMessagesFactory.Get();
|
||||
using (var scope = ScopeProvider.CreateScope())
|
||||
{
|
||||
var deleteEventArgs = new DeleteEventArgs<IDataType>(dataType);
|
||||
if (scope.Events.DispatchCancelable(Deleting, this, deleteEventArgs))
|
||||
var deletingDataTypeNotification = new DataTypeDeletingNotification(dataType, evtMsgs);
|
||||
if (scope.Notifications.PublishCancelable(deletingDataTypeNotification))
|
||||
{
|
||||
scope.Complete();
|
||||
return;
|
||||
@@ -506,8 +520,8 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
|
||||
_dataTypeRepository.Delete(dataType);
|
||||
|
||||
deleteEventArgs.CanCancel = false;
|
||||
scope.Events.Dispatch(Deleted, this, deleteEventArgs);
|
||||
scope.Notifications.Publish(new DataTypeDeletedNotification(dataType, evtMsgs).WithStateFrom(deletingDataTypeNotification));
|
||||
|
||||
Audit(AuditType.Delete, userId, dataType.Id);
|
||||
|
||||
scope.Complete();
|
||||
@@ -527,42 +541,5 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
_auditRepository.Save(new AuditItem(objectId, type, userId, ObjectTypes.GetName(UmbracoObjectTypes.DataType)));
|
||||
}
|
||||
|
||||
#region Event Handlers
|
||||
|
||||
public static event TypedEventHandler<IDataTypeService, SaveEventArgs<EntityContainer>> SavingContainer;
|
||||
public static event TypedEventHandler<IDataTypeService, SaveEventArgs<EntityContainer>> SavedContainer;
|
||||
public static event TypedEventHandler<IDataTypeService, DeleteEventArgs<EntityContainer>> DeletingContainer;
|
||||
public static event TypedEventHandler<IDataTypeService, DeleteEventArgs<EntityContainer>> DeletedContainer;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs before Delete
|
||||
/// </summary>
|
||||
public static event TypedEventHandler<IDataTypeService, DeleteEventArgs<IDataType>> Deleting;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs after Delete
|
||||
/// </summary>
|
||||
public static event TypedEventHandler<IDataTypeService, DeleteEventArgs<IDataType>> Deleted;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs before Save
|
||||
/// </summary>
|
||||
public static event TypedEventHandler<IDataTypeService, SaveEventArgs<IDataType>> Saving;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs after Save
|
||||
/// </summary>
|
||||
public static event TypedEventHandler<IDataTypeService, SaveEventArgs<IDataType>> Saved;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs before Move
|
||||
/// </summary>
|
||||
public static event TypedEventHandler<IDataTypeService, MoveEventArgs<IDataType>> Moving;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs after Move
|
||||
/// </summary>
|
||||
public static event TypedEventHandler<IDataTypeService, MoveEventArgs<IDataType>> Moved;
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ using Umbraco.Cms.Core.Events;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Persistence.Repositories;
|
||||
using Umbraco.Cms.Core.Scoping;
|
||||
using Umbraco.Cms.Infrastructure.Services.Notifications;
|
||||
using Umbraco.Extensions;
|
||||
|
||||
namespace Umbraco.Cms.Core.Services.Implement
|
||||
@@ -13,14 +14,19 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
/// <summary>
|
||||
/// Represents the Localization Service, which is an easy access to operations involving <see cref="Language"/> and <see cref="DictionaryItem"/>
|
||||
/// </summary>
|
||||
public class LocalizationService : RepositoryService, ILocalizationService
|
||||
internal class LocalizationService : RepositoryService, ILocalizationService
|
||||
{
|
||||
private readonly IDictionaryRepository _dictionaryRepository;
|
||||
private readonly ILanguageRepository _languageRepository;
|
||||
private readonly IAuditRepository _auditRepository;
|
||||
|
||||
public LocalizationService(IScopeProvider provider, ILoggerFactory loggerFactory, IEventMessagesFactory eventMessagesFactory,
|
||||
IDictionaryRepository dictionaryRepository, IAuditRepository auditRepository, ILanguageRepository languageRepository)
|
||||
public LocalizationService(
|
||||
IScopeProvider provider,
|
||||
ILoggerFactory loggerFactory,
|
||||
IEventMessagesFactory eventMessagesFactory,
|
||||
IDictionaryRepository dictionaryRepository,
|
||||
IAuditRepository auditRepository,
|
||||
ILanguageRepository languageRepository)
|
||||
: base(provider, loggerFactory, eventMessagesFactory)
|
||||
{
|
||||
_dictionaryRepository = dictionaryRepository;
|
||||
@@ -88,9 +94,11 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
|
||||
item.Translations = translations;
|
||||
}
|
||||
var saveEventArgs = new SaveEventArgs<IDictionaryItem>(item);
|
||||
|
||||
if (scope.Events.DispatchCancelable(SavingDictionaryItem, this, saveEventArgs))
|
||||
EventMessages eventMessages = EventMessagesFactory.Get();
|
||||
var savingNotification = new DictionaryItemSavingNotification(item, eventMessages);
|
||||
|
||||
if (scope.Notifications.PublishCancelable(savingNotification))
|
||||
{
|
||||
scope.Complete();
|
||||
return item;
|
||||
@@ -100,8 +108,7 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
// ensure the lazy Language callback is assigned
|
||||
EnsureDictionaryItemLanguageCallback(item);
|
||||
|
||||
saveEventArgs.CanCancel = false;
|
||||
scope.Events.Dispatch(SavedDictionaryItem, this, saveEventArgs);
|
||||
scope.Notifications.Publish(new DictionaryItemSavedNotification(item, eventMessages).WithStateFrom(savingNotification));
|
||||
|
||||
scope.Complete();
|
||||
|
||||
@@ -232,7 +239,9 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
{
|
||||
using (var scope = ScopeProvider.CreateScope())
|
||||
{
|
||||
if (scope.Events.DispatchCancelable(SavingDictionaryItem, this, new SaveEventArgs<IDictionaryItem>(dictionaryItem)))
|
||||
EventMessages eventMessages = EventMessagesFactory.Get();
|
||||
var savingNotification = new DictionaryItemSavingNotification(dictionaryItem, eventMessages);
|
||||
if (scope.Notifications.PublishCancelable(savingNotification))
|
||||
{
|
||||
scope.Complete();
|
||||
return;
|
||||
@@ -244,7 +253,7 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
// ensure the lazy Language callback is assigned
|
||||
|
||||
EnsureDictionaryItemLanguageCallback(dictionaryItem);
|
||||
scope.Events.Dispatch(SavedDictionaryItem, this, new SaveEventArgs<IDictionaryItem>(dictionaryItem, false));
|
||||
scope.Notifications.Publish(new DictionaryItemSavedNotification(dictionaryItem, eventMessages).WithStateFrom(savingNotification));
|
||||
|
||||
Audit(AuditType.Save, "Save DictionaryItem", userId, dictionaryItem.Id, "DictionaryItem");
|
||||
scope.Complete();
|
||||
@@ -261,16 +270,16 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
{
|
||||
using (var scope = ScopeProvider.CreateScope())
|
||||
{
|
||||
var deleteEventArgs = new DeleteEventArgs<IDictionaryItem>(dictionaryItem);
|
||||
if (scope.Events.DispatchCancelable(DeletingDictionaryItem, this, deleteEventArgs))
|
||||
EventMessages eventMessages = EventMessagesFactory.Get();
|
||||
var deletingNotification = new DictionaryItemDeletingNotification(dictionaryItem, eventMessages);
|
||||
if (scope.Notifications.PublishCancelable(deletingNotification))
|
||||
{
|
||||
scope.Complete();
|
||||
return;
|
||||
}
|
||||
|
||||
_dictionaryRepository.Delete(dictionaryItem);
|
||||
deleteEventArgs.CanCancel = false;
|
||||
scope.Events.Dispatch(DeletedDictionaryItem, this, deleteEventArgs);
|
||||
scope.Notifications.Publish(new DictionaryItemDeletedNotification(dictionaryItem, eventMessages).WithStateFrom(deletingNotification));
|
||||
|
||||
Audit(AuditType.Delete, "Delete DictionaryItem", userId, dictionaryItem.Id, "DictionaryItem");
|
||||
|
||||
@@ -374,16 +383,16 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
throw new InvalidOperationException($"Cannot save language {language.IsoCode} with fallback {languages[language.FallbackLanguageId.Value].IsoCode} as it would create a fallback cycle.");
|
||||
}
|
||||
|
||||
var saveEventArgs = new SaveEventArgs<ILanguage>(language);
|
||||
if (scope.Events.DispatchCancelable(SavingLanguage, this, saveEventArgs))
|
||||
EventMessages eventMessages = EventMessagesFactory.Get();
|
||||
var savingNotification = new LanguageSavingNotification(language, eventMessages);
|
||||
if (scope.Notifications.PublishCancelable(savingNotification))
|
||||
{
|
||||
scope.Complete();
|
||||
return;
|
||||
}
|
||||
|
||||
_languageRepository.Save(language);
|
||||
saveEventArgs.CanCancel = false;
|
||||
scope.Events.Dispatch(SavedLanguage, this, saveEventArgs);
|
||||
scope.Notifications.Publish(new LanguageSavedNotification(language, eventMessages).WithStateFrom(savingNotification));
|
||||
|
||||
Audit(AuditType.Save, "Save Language", userId, language.Id, ObjectTypes.GetName(UmbracoObjectTypes.Language));
|
||||
|
||||
@@ -417,8 +426,9 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
// write-lock languages to guard against race conds when dealing with default language
|
||||
scope.WriteLock(Cms.Core.Constants.Locks.Languages);
|
||||
|
||||
var deleteEventArgs = new DeleteEventArgs<ILanguage>(language);
|
||||
if (scope.Events.DispatchCancelable(DeletingLanguage, this, deleteEventArgs))
|
||||
EventMessages eventMessages = EventMessagesFactory.Get();
|
||||
var deletingLanguageNotification = new LanguageDeletingNotification(language, eventMessages);
|
||||
if (scope.Notifications.PublishCancelable(deletingLanguageNotification))
|
||||
{
|
||||
scope.Complete();
|
||||
return;
|
||||
@@ -426,9 +436,8 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
|
||||
// NOTE: Other than the fall-back language, there aren't any other constraints in the db, so possible references aren't deleted
|
||||
_languageRepository.Delete(language);
|
||||
deleteEventArgs.CanCancel = false;
|
||||
|
||||
scope.Events.Dispatch(DeletedLanguage, this, deleteEventArgs);
|
||||
scope.Notifications.Publish(new LanguageDeletedNotification(language, eventMessages).WithStateFrom(deletingLanguageNotification));
|
||||
|
||||
Audit(AuditType.Delete, "Delete Language", userId, language.Id, ObjectTypes.GetName(UmbracoObjectTypes.Language));
|
||||
scope.Complete();
|
||||
@@ -463,47 +472,5 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
return _dictionaryRepository.GetDictionaryItemKeyMap();
|
||||
}
|
||||
}
|
||||
|
||||
#region Events
|
||||
/// <summary>
|
||||
/// Occurs before Delete
|
||||
/// </summary>
|
||||
public static event TypedEventHandler<ILocalizationService, DeleteEventArgs<ILanguage>> DeletingLanguage;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs after Delete
|
||||
/// </summary>
|
||||
public static event TypedEventHandler<ILocalizationService, DeleteEventArgs<ILanguage>> DeletedLanguage;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs before Delete
|
||||
/// </summary>
|
||||
public static event TypedEventHandler<ILocalizationService, DeleteEventArgs<IDictionaryItem>> DeletingDictionaryItem;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs after Delete
|
||||
/// </summary>
|
||||
public static event TypedEventHandler<ILocalizationService, DeleteEventArgs<IDictionaryItem>> DeletedDictionaryItem;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs before Save
|
||||
/// </summary>
|
||||
public static event TypedEventHandler<ILocalizationService, SaveEventArgs<IDictionaryItem>> SavingDictionaryItem;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs after Save
|
||||
/// </summary>
|
||||
public static event TypedEventHandler<ILocalizationService, SaveEventArgs<IDictionaryItem>> SavedDictionaryItem;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs before Save
|
||||
/// </summary>
|
||||
public static event TypedEventHandler<ILocalizationService, SaveEventArgs<ILanguage>> SavingLanguage;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs after Save
|
||||
/// </summary>
|
||||
public static event TypedEventHandler<ILocalizationService, SaveEventArgs<ILanguage>> SavedLanguage;
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.Extensions.Logging;
|
||||
@@ -6,40 +6,18 @@ using Umbraco.Cms.Core.Events;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Persistence.Repositories;
|
||||
using Umbraco.Cms.Core.Scoping;
|
||||
using Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement;
|
||||
using Umbraco.Cms.Infrastructure.Services.Notifications;
|
||||
|
||||
namespace Umbraco.Cms.Core.Services.Implement
|
||||
{
|
||||
public class MemberGroupService : RepositoryService, IMemberGroupService
|
||||
internal class MemberGroupService : RepositoryService, IMemberGroupService
|
||||
{
|
||||
private readonly IMemberGroupRepository _memberGroupRepository;
|
||||
|
||||
public MemberGroupService(IScopeProvider provider, ILoggerFactory loggerFactory, IEventMessagesFactory eventMessagesFactory,
|
||||
IMemberGroupRepository memberGroupRepository)
|
||||
: base(provider, loggerFactory, eventMessagesFactory)
|
||||
{
|
||||
: base(provider, loggerFactory, eventMessagesFactory) =>
|
||||
_memberGroupRepository = memberGroupRepository;
|
||||
//Proxy events!
|
||||
MemberGroupRepository.SavedMemberGroup += MemberGroupRepository_SavedMemberGroup;
|
||||
MemberGroupRepository.SavingMemberGroup += MemberGroupRepository_SavingMemberGroup;
|
||||
}
|
||||
|
||||
#region Proxy event handlers
|
||||
|
||||
void MemberGroupRepository_SavingMemberGroup(IMemberGroupRepository sender, SaveEventArgs<IMemberGroup> e)
|
||||
{
|
||||
if (Saving.IsRaisedEventCancelled(new SaveEventArgs<IMemberGroup>(e.SavedEntities), this))
|
||||
e.Cancel = true;
|
||||
}
|
||||
|
||||
void MemberGroupRepository_SavedMemberGroup(IMemberGroupRepository sender, SaveEventArgs<IMemberGroup> e)
|
||||
{
|
||||
// same as above!
|
||||
|
||||
Saved.RaiseEvent(new SaveEventArgs<IMemberGroup>(e.SavedEntities, false), this);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public IEnumerable<IMemberGroup> GetAll()
|
||||
{
|
||||
@@ -92,10 +70,13 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
{
|
||||
throw new InvalidOperationException("The name of a MemberGroup can not be empty");
|
||||
}
|
||||
|
||||
var evtMsgs = EventMessagesFactory.Get();
|
||||
|
||||
using (var scope = ScopeProvider.CreateScope())
|
||||
{
|
||||
var saveEventArgs = new SaveEventArgs<IMemberGroup>(memberGroup);
|
||||
if (raiseEvents && scope.Events.DispatchCancelable(Saving, this, saveEventArgs))
|
||||
var savingNotification = new MemberGroupSavingNotification(memberGroup, evtMsgs);
|
||||
if (raiseEvents && scope.Notifications.PublishCancelable(savingNotification))
|
||||
{
|
||||
scope.Complete();
|
||||
return;
|
||||
@@ -106,18 +87,19 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
|
||||
if (raiseEvents)
|
||||
{
|
||||
saveEventArgs.CanCancel = false;
|
||||
scope.Events.Dispatch(Saved, this, saveEventArgs);
|
||||
scope.Notifications.Publish(new MemberGroupSavedNotification(memberGroup, evtMsgs).WithStateFrom(savingNotification));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Delete(IMemberGroup memberGroup)
|
||||
{
|
||||
var evtMsgs = EventMessagesFactory.Get();
|
||||
|
||||
using (var scope = ScopeProvider.CreateScope())
|
||||
{
|
||||
var deleteEventArgs = new DeleteEventArgs<IMemberGroup>(memberGroup);
|
||||
if (scope.Events.DispatchCancelable(Deleting, this, deleteEventArgs))
|
||||
var deletingNotification = new MemberGroupDeletingNotification(memberGroup, evtMsgs);
|
||||
if (scope.Notifications.PublishCancelable(deletingNotification))
|
||||
{
|
||||
scope.Complete();
|
||||
return;
|
||||
@@ -125,35 +107,9 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
|
||||
_memberGroupRepository.Delete(memberGroup);
|
||||
scope.Complete();
|
||||
deleteEventArgs.CanCancel = false;
|
||||
scope.Events.Dispatch(Deleted, this, deleteEventArgs);
|
||||
|
||||
scope.Notifications.Publish(new MemberGroupDeletedNotification(memberGroup, evtMsgs).WithStateFrom(deletingNotification));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Occurs before Delete of a member group
|
||||
/// </summary>
|
||||
public static event TypedEventHandler<IMemberGroupService, DeleteEventArgs<IMemberGroup>> Deleting;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs after Delete of a member group
|
||||
/// </summary>
|
||||
public static event TypedEventHandler<IMemberGroupService, DeleteEventArgs<IMemberGroup>> Deleted;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs before Save of a member group
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// We need to proxy these events because the events need to take place at the repo level
|
||||
/// </remarks>
|
||||
public static event TypedEventHandler<IMemberGroupService, SaveEventArgs<IMemberGroup>> Saving;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs after Save of a member group
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// We need to proxy these events because the events need to take place at the repo level
|
||||
/// </remarks>
|
||||
public static event TypedEventHandler<IMemberGroupService, SaveEventArgs<IMemberGroup>> Saved;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ using Umbraco.Cms.Core.Persistence.Querying;
|
||||
using Umbraco.Cms.Core.Persistence.Repositories;
|
||||
using Umbraco.Cms.Core.Scoping;
|
||||
using Umbraco.Cms.Infrastructure.Persistence.Querying;
|
||||
using Umbraco.Cms.Infrastructure.Services.Notifications;
|
||||
using Umbraco.Extensions;
|
||||
|
||||
namespace Umbraco.Cms.Core.Services.Implement
|
||||
@@ -22,7 +23,6 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
private readonly IMemberTypeRepository _memberTypeRepository;
|
||||
private readonly IMemberGroupRepository _memberGroupRepository;
|
||||
private readonly IAuditRepository _auditRepository;
|
||||
private readonly IMemberTypeService _memberTypeService;
|
||||
|
||||
private readonly IMemberGroupService _memberGroupService;
|
||||
|
||||
@@ -773,10 +773,12 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
member.Username = member.Username.Trim();
|
||||
member.Email = member.Email.Trim();
|
||||
|
||||
var evtMsgs = EventMessagesFactory.Get();
|
||||
|
||||
using (IScope scope = ScopeProvider.CreateScope())
|
||||
{
|
||||
var saveEventArgs = new SaveEventArgs<IMember>(member);
|
||||
if (raiseEvents && scope.Events.DispatchCancelable(Saving, this, saveEventArgs))
|
||||
var savingNotification = new MemberSavingNotification(member, evtMsgs);
|
||||
if (raiseEvents && scope.Notifications.PublishCancelable(savingNotification))
|
||||
{
|
||||
scope.Complete();
|
||||
return;
|
||||
@@ -793,8 +795,7 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
|
||||
if (raiseEvents)
|
||||
{
|
||||
saveEventArgs.CanCancel = false;
|
||||
scope.Events.Dispatch(Saved, this, saveEventArgs);
|
||||
scope.Notifications.Publish(new MemberSavedNotification(member, evtMsgs).WithStateFrom(savingNotification));
|
||||
}
|
||||
|
||||
Audit(AuditType.Save, 0, member.Id);
|
||||
@@ -808,10 +809,12 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
{
|
||||
var membersA = members.ToArray();
|
||||
|
||||
var evtMsgs = EventMessagesFactory.Get();
|
||||
|
||||
using (var scope = ScopeProvider.CreateScope())
|
||||
{
|
||||
var saveEventArgs = new SaveEventArgs<IMember>(membersA);
|
||||
if (raiseEvents && scope.Events.DispatchCancelable(Saving, this, saveEventArgs))
|
||||
var savingNotification = new MemberSavingNotification(membersA, evtMsgs);
|
||||
if (raiseEvents && scope.Notifications.PublishCancelable(savingNotification))
|
||||
{
|
||||
scope.Complete();
|
||||
return;
|
||||
@@ -830,8 +833,7 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
|
||||
if (raiseEvents)
|
||||
{
|
||||
saveEventArgs.CanCancel = false;
|
||||
scope.Events.Dispatch(Saved, this, saveEventArgs);
|
||||
scope.Notifications.Publish(new MemberSavedNotification(membersA, evtMsgs).WithStateFrom(savingNotification));
|
||||
}
|
||||
Audit(AuditType.Save, 0, -1, "Save multiple Members");
|
||||
|
||||
@@ -849,32 +851,30 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
/// <param name="member"><see cref="IMember"/> to Delete</param>
|
||||
public void Delete(IMember member)
|
||||
{
|
||||
var evtMsgs = EventMessagesFactory.Get();
|
||||
|
||||
using (var scope = ScopeProvider.CreateScope())
|
||||
{
|
||||
var deleteEventArgs = new DeleteEventArgs<IMember>(member);
|
||||
if (scope.Events.DispatchCancelable(Deleting, this, deleteEventArgs))
|
||||
var deletingNotification = new MemberDeletingNotification(member, evtMsgs);
|
||||
if (scope.Notifications.PublishCancelable(deletingNotification))
|
||||
{
|
||||
scope.Complete();
|
||||
return;
|
||||
}
|
||||
|
||||
scope.WriteLock(Constants.Locks.MemberTree);
|
||||
DeleteLocked(scope, member, deleteEventArgs);
|
||||
DeleteLocked(scope, member, evtMsgs, deletingNotification.State);
|
||||
|
||||
Audit(AuditType.Delete, 0, member.Id);
|
||||
scope.Complete();
|
||||
}
|
||||
}
|
||||
|
||||
private void DeleteLocked(IScope scope, IMember member, DeleteEventArgs<IMember> args = null)
|
||||
private void DeleteLocked(IScope scope, IMember member, EventMessages evtMsgs, IDictionary<string, object> notificationState = null)
|
||||
{
|
||||
// a member has no descendants
|
||||
_memberRepository.Delete(member);
|
||||
if (args == null)
|
||||
args = new DeleteEventArgs<IMember>(member, false); // raise event & get flagged files
|
||||
else
|
||||
args.CanCancel = false;
|
||||
scope.Events.Dispatch(Deleted, this, args);
|
||||
scope.Notifications.Publish(new MemberDeletedNotification(member, evtMsgs).WithState(notificationState));
|
||||
|
||||
// media files deleted by QueuingEventDispatcher
|
||||
}
|
||||
@@ -1017,7 +1017,7 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
scope.WriteLock(Constants.Locks.MemberTree);
|
||||
int[] ids = _memberGroupRepository.GetMemberIds(usernames);
|
||||
_memberGroupRepository.AssignRoles(ids, roleNames);
|
||||
scope.Events.Dispatch(AssignedRoles, this, new RolesEventArgs(ids, roleNames), nameof(AssignedRoles));
|
||||
scope.Notifications.Publish(new AssignedMemberRolesNotification(ids, roleNames));
|
||||
scope.Complete();
|
||||
}
|
||||
}
|
||||
@@ -1031,7 +1031,7 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
scope.WriteLock(Constants.Locks.MemberTree);
|
||||
int[] ids = _memberGroupRepository.GetMemberIds(usernames);
|
||||
_memberGroupRepository.DissociateRoles(ids, roleNames);
|
||||
scope.Events.Dispatch(RemovedRoles, this, new RolesEventArgs(ids, roleNames), nameof(RemovedRoles));
|
||||
scope.Notifications.Publish(new RemovedMemberRolesNotification(ids, roleNames));
|
||||
scope.Complete();
|
||||
}
|
||||
}
|
||||
@@ -1044,7 +1044,7 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
{
|
||||
scope.WriteLock(Constants.Locks.MemberTree);
|
||||
_memberGroupRepository.AssignRoles(memberIds, roleNames);
|
||||
scope.Events.Dispatch(AssignedRoles, this, new RolesEventArgs(memberIds, roleNames), nameof(AssignedRoles));
|
||||
scope.Notifications.Publish(new AssignedMemberRolesNotification(memberIds, roleNames));
|
||||
scope.Complete();
|
||||
}
|
||||
}
|
||||
@@ -1057,7 +1057,7 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
{
|
||||
scope.WriteLock(Constants.Locks.MemberTree);
|
||||
_memberGroupRepository.DissociateRoles(memberIds, roleNames);
|
||||
scope.Events.Dispatch(RemovedRoles, this, new RolesEventArgs(memberIds, roleNames), nameof(RemovedRoles));
|
||||
scope.Notifications.Publish(new RemovedMemberRolesNotification(memberIds, roleNames));
|
||||
scope.Complete();
|
||||
}
|
||||
}
|
||||
@@ -1070,45 +1070,6 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
|
||||
#endregion
|
||||
|
||||
#region Event Handlers
|
||||
|
||||
/// <summary>
|
||||
/// Occurs before Delete
|
||||
/// </summary>
|
||||
public static event TypedEventHandler<IMemberService, DeleteEventArgs<IMember>> Deleting;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs after Delete
|
||||
/// </summary>
|
||||
public static event TypedEventHandler<IMemberService, DeleteEventArgs<IMember>> Deleted;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs before Save
|
||||
/// </summary>
|
||||
public static event TypedEventHandler<IMemberService, SaveEventArgs<IMember>> Saving;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs after Save
|
||||
/// </summary>
|
||||
public static event TypedEventHandler<IMemberService, SaveEventArgs<IMember>> Saved;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs after roles have been assigned.
|
||||
/// </summary>
|
||||
public static event TypedEventHandler<IMemberService, RolesEventArgs> AssignedRoles;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs after roles have been removed.
|
||||
/// </summary>
|
||||
public static event TypedEventHandler<IMemberService, RolesEventArgs> RemovedRoles;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs after members have been exported.
|
||||
/// </summary>
|
||||
public static event TypedEventHandler<IMemberService, ExportedMemberEventArgs> Exported;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Membership
|
||||
|
||||
|
||||
@@ -1145,7 +1106,7 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
Properties = new List<MemberExportProperty>(GetPropertyExportItems(member))
|
||||
};
|
||||
|
||||
scope.Events.Dispatch(Exported, this, new ExportedMemberEventArgs(member, model));
|
||||
scope.Notifications.Publish(new ExportedMemberNotification(member, model));
|
||||
|
||||
return model;
|
||||
}
|
||||
@@ -1187,6 +1148,8 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
/// <param name="memberTypeId">Id of the MemberType</param>
|
||||
public void DeleteMembersOfType(int memberTypeId)
|
||||
{
|
||||
var evtMsgs = EventMessagesFactory.Get();
|
||||
|
||||
// note: no tree to manage here
|
||||
using (IScope scope = ScopeProvider.CreateScope())
|
||||
{
|
||||
@@ -1196,9 +1159,8 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
IQuery<IMember> query = Query<IMember>().Where(x => x.ContentTypeId == memberTypeId);
|
||||
|
||||
IMember[] members = _memberRepository.Get(query).ToArray();
|
||||
var deleteEventArgs = new DeleteEventArgs<IMember>(members);
|
||||
|
||||
if (scope.Events.DispatchCancelable(Deleting, this, deleteEventArgs))
|
||||
if (scope.Notifications.PublishCancelable(new MemberDeletingNotification(members, evtMsgs)))
|
||||
{
|
||||
scope.Complete();
|
||||
return;
|
||||
@@ -1208,7 +1170,7 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
{
|
||||
// delete media
|
||||
// triggers the deleted event (and handles the files)
|
||||
DeleteLocked(scope, member);
|
||||
DeleteLocked(scope, member, evtMsgs);
|
||||
}
|
||||
|
||||
scope.Complete();
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.Extensions.Logging;
|
||||
@@ -6,11 +6,12 @@ using Umbraco.Cms.Core.Events;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Persistence.Repositories;
|
||||
using Umbraco.Cms.Core.Scoping;
|
||||
using Umbraco.Cms.Infrastructure.Services.Notifications;
|
||||
using Umbraco.Extensions;
|
||||
|
||||
namespace Umbraco.Cms.Core.Services.Implement
|
||||
{
|
||||
public class PublicAccessService : RepositoryService, IPublicAccessService
|
||||
internal class PublicAccessService : RepositoryService, IPublicAccessService
|
||||
{
|
||||
private readonly IPublicAccessRepository _publicAccessRepository;
|
||||
|
||||
@@ -114,7 +115,7 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
{
|
||||
entry = _publicAccessRepository.GetMany().FirstOrDefault(x => x.ProtectedNodeId == content.Id);
|
||||
if (entry == null)
|
||||
return OperationResult.Attempt.Cannot<PublicAccessEntry>(evtMsgs); // causes rollback // causes rollback
|
||||
return OperationResult.Attempt.Cannot<PublicAccessEntry>(evtMsgs); // causes rollback
|
||||
|
||||
var existingRule = entry.Rules.FirstOrDefault(x => x.RuleType == ruleType && x.RuleValue == ruleValue);
|
||||
if (existingRule == null)
|
||||
@@ -127,8 +128,8 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
return OperationResult.Attempt.Succeed(evtMsgs, entry);
|
||||
}
|
||||
|
||||
var saveEventArgs = new SaveEventArgs<PublicAccessEntry>(entry, evtMsgs);
|
||||
if (scope.Events.DispatchCancelable(Saving, this, saveEventArgs))
|
||||
var savingNotifiation = new PublicAccessEntrySavingNotification(entry, evtMsgs);
|
||||
if (scope.Notifications.PublishCancelable(savingNotifiation))
|
||||
{
|
||||
scope.Complete();
|
||||
return OperationResult.Attempt.Cancel(evtMsgs, entry);
|
||||
@@ -138,8 +139,7 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
|
||||
scope.Complete();
|
||||
|
||||
saveEventArgs.CanCancel = false;
|
||||
scope.Events.Dispatch(Saved, this, saveEventArgs);
|
||||
scope.Notifications.Publish(new PublicAccessEntrySavedNotification(entry, evtMsgs).WithStateFrom(savingNotifiation));
|
||||
}
|
||||
|
||||
return OperationResult.Attempt.Succeed(evtMsgs, entry);
|
||||
@@ -165,8 +165,8 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
|
||||
entry.RemoveRule(existingRule);
|
||||
|
||||
var saveEventArgs = new SaveEventArgs<PublicAccessEntry>(entry, evtMsgs);
|
||||
if (scope.Events.DispatchCancelable(Saving, this, saveEventArgs))
|
||||
var savingNotifiation = new PublicAccessEntrySavingNotification(entry, evtMsgs);
|
||||
if (scope.Notifications.PublishCancelable(savingNotifiation))
|
||||
{
|
||||
scope.Complete();
|
||||
return OperationResult.Attempt.Cancel(evtMsgs);
|
||||
@@ -175,8 +175,7 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
_publicAccessRepository.Save(entry);
|
||||
scope.Complete();
|
||||
|
||||
saveEventArgs.CanCancel = false;
|
||||
scope.Events.Dispatch(Saved, this, saveEventArgs);
|
||||
scope.Notifications.Publish(new PublicAccessEntrySavedNotification(entry, evtMsgs).WithStateFrom(savingNotifiation));
|
||||
}
|
||||
|
||||
return OperationResult.Attempt.Succeed(evtMsgs);
|
||||
@@ -192,8 +191,8 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
|
||||
using (var scope = ScopeProvider.CreateScope())
|
||||
{
|
||||
var saveEventArgs = new SaveEventArgs<PublicAccessEntry>(entry, evtMsgs);
|
||||
if (scope.Events.DispatchCancelable(Saving, this, saveEventArgs))
|
||||
var savingNotifiation = new PublicAccessEntrySavingNotification(entry, evtMsgs);
|
||||
if (scope.Notifications.PublishCancelable(savingNotifiation))
|
||||
{
|
||||
scope.Complete();
|
||||
return OperationResult.Attempt.Cancel(evtMsgs);
|
||||
@@ -202,8 +201,7 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
_publicAccessRepository.Save(entry);
|
||||
scope.Complete();
|
||||
|
||||
saveEventArgs.CanCancel = false;
|
||||
scope.Events.Dispatch(Saved, this, saveEventArgs);
|
||||
scope.Notifications.Publish(new PublicAccessEntrySavedNotification(entry, evtMsgs).WithStateFrom(savingNotifiation));
|
||||
}
|
||||
|
||||
return OperationResult.Attempt.Succeed(evtMsgs);
|
||||
@@ -219,8 +217,8 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
|
||||
using (var scope = ScopeProvider.CreateScope())
|
||||
{
|
||||
var deleteEventArgs = new DeleteEventArgs<PublicAccessEntry>(entry, evtMsgs);
|
||||
if (scope.Events.DispatchCancelable(Deleting, this, deleteEventArgs))
|
||||
var deletingNotification = new PublicAccessEntryDeletingNotification(entry, evtMsgs);
|
||||
if (scope.Notifications.PublishCancelable(deletingNotification))
|
||||
{
|
||||
scope.Complete();
|
||||
return OperationResult.Attempt.Cancel(evtMsgs);
|
||||
@@ -229,33 +227,10 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
_publicAccessRepository.Delete(entry);
|
||||
scope.Complete();
|
||||
|
||||
deleteEventArgs.CanCancel = false;
|
||||
scope.Events.Dispatch(Deleted, this, deleteEventArgs);
|
||||
scope.Notifications.Publish(new PublicAccessEntryDeletedNotification(entry, evtMsgs).WithStateFrom(deletingNotification));
|
||||
}
|
||||
|
||||
return OperationResult.Attempt.Succeed(evtMsgs);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Occurs before Save
|
||||
/// </summary>
|
||||
public static event TypedEventHandler<IPublicAccessService, SaveEventArgs<PublicAccessEntry>> Saving;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs after Save
|
||||
/// </summary>
|
||||
public static event TypedEventHandler<IPublicAccessService, SaveEventArgs<PublicAccessEntry>> Saved;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs before Delete
|
||||
/// </summary>
|
||||
public static event TypedEventHandler<IPublicAccessService, DeleteEventArgs<PublicAccessEntry>> Deleting;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs after Delete
|
||||
/// </summary>
|
||||
public static event TypedEventHandler<IPublicAccessService, DeleteEventArgs<PublicAccessEntry>> Deleted;
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ using Umbraco.Cms.Core.Persistence.Repositories;
|
||||
using Umbraco.Cms.Core.Scoping;
|
||||
using Umbraco.Cms.Infrastructure.Persistence.Querying;
|
||||
using Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement;
|
||||
using Umbraco.Cms.Infrastructure.Services.Notifications;
|
||||
using Umbraco.Extensions;
|
||||
|
||||
namespace Umbraco.Cms.Core.Services.Implement
|
||||
@@ -21,7 +22,7 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
/// <summary>
|
||||
/// Represents the UserService, which is an easy access to operations involving <see cref="IProfile"/>, <see cref="IMembershipUser"/> and eventually Backoffice Users.
|
||||
/// </summary>
|
||||
public class UserService : RepositoryService, IUserService
|
||||
internal class UserService : RepositoryService, IUserService
|
||||
{
|
||||
private readonly IUserRepository _userRepository;
|
||||
private readonly IUserGroupRepository _userGroupRepository;
|
||||
@@ -109,6 +110,8 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
if (username == null) throw new ArgumentNullException(nameof(username));
|
||||
if (string.IsNullOrWhiteSpace(username)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(username));
|
||||
|
||||
var evtMsgs = EventMessagesFactory.Get();
|
||||
|
||||
// TODO: PUT lock here!!
|
||||
|
||||
User user;
|
||||
@@ -129,8 +132,8 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
IsApproved = isApproved
|
||||
};
|
||||
|
||||
var saveEventArgs = new SaveEventArgs<IUser>(user);
|
||||
if (scope.Events.DispatchCancelable(SavingUser, this, saveEventArgs))
|
||||
var savingNotification = new UserSavingNotification(user, evtMsgs);
|
||||
if (scope.Notifications.PublishCancelable(savingNotification))
|
||||
{
|
||||
scope.Complete();
|
||||
return user;
|
||||
@@ -138,8 +141,7 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
|
||||
_userRepository.Save(user);
|
||||
|
||||
saveEventArgs.CanCancel = false;
|
||||
scope.Events.Dispatch(SavedUser, this, saveEventArgs);
|
||||
scope.Notifications.Publish(new UserSavedNotification(user, evtMsgs).WithStateFrom(savingNotification));
|
||||
scope.Complete();
|
||||
}
|
||||
|
||||
@@ -239,10 +241,12 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
}
|
||||
else
|
||||
{
|
||||
var evtMsgs = EventMessagesFactory.Get();
|
||||
|
||||
using (var scope = ScopeProvider.CreateScope())
|
||||
{
|
||||
var deleteEventArgs = new DeleteEventArgs<IUser>(user);
|
||||
if (scope.Events.DispatchCancelable(DeletingUser, this, deleteEventArgs))
|
||||
var deletingNotification = new UserDeletingNotification(user, evtMsgs);
|
||||
if (scope.Notifications.PublishCancelable(deletingNotification))
|
||||
{
|
||||
scope.Complete();
|
||||
return;
|
||||
@@ -250,8 +254,7 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
|
||||
_userRepository.Delete(user);
|
||||
|
||||
deleteEventArgs.CanCancel = false;
|
||||
scope.Events.Dispatch(DeletedUser, this, deleteEventArgs);
|
||||
scope.Notifications.Publish(new UserDeletedNotification(user, evtMsgs).WithStateFrom(deletingNotification));
|
||||
scope.Complete();
|
||||
}
|
||||
}
|
||||
@@ -272,10 +275,12 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
/// Default is <c>True</c> otherwise set to <c>False</c> to not raise events</param>
|
||||
public void Save(IUser entity, bool raiseEvents = true)
|
||||
{
|
||||
var evtMsgs = EventMessagesFactory.Get();
|
||||
|
||||
using (var scope = ScopeProvider.CreateScope())
|
||||
{
|
||||
var saveEventArgs = new SaveEventArgs<IUser>(entity);
|
||||
if (raiseEvents && scope.Events.DispatchCancelable(SavingUser, this, saveEventArgs))
|
||||
var savingNotification = new UserSavingNotification(entity, evtMsgs);
|
||||
if (raiseEvents && scope.Notifications.PublishCancelable(savingNotification))
|
||||
{
|
||||
scope.Complete();
|
||||
return;
|
||||
@@ -292,8 +297,7 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
_userRepository.Save(entity);
|
||||
if (raiseEvents)
|
||||
{
|
||||
saveEventArgs.CanCancel = false;
|
||||
scope.Events.Dispatch(SavedUser, this, saveEventArgs);
|
||||
scope.Notifications.Publish(new UserSavedNotification(entity, evtMsgs).WithStateFrom(savingNotification));
|
||||
}
|
||||
|
||||
scope.Complete();
|
||||
@@ -319,12 +323,14 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
/// Default is <c>True</c> otherwise set to <c>False</c> to not raise events</param>
|
||||
public void Save(IEnumerable<IUser> entities, bool raiseEvents = true)
|
||||
{
|
||||
var evtMsgs = EventMessagesFactory.Get();
|
||||
|
||||
var entitiesA = entities.ToArray();
|
||||
|
||||
using (var scope = ScopeProvider.CreateScope())
|
||||
{
|
||||
var saveEventArgs = new SaveEventArgs<IUser>(entitiesA);
|
||||
if (raiseEvents && scope.Events.DispatchCancelable(SavingUser, this, saveEventArgs))
|
||||
var savingNotification = new UserSavingNotification(entitiesA, evtMsgs);
|
||||
if (raiseEvents && scope.Notifications.PublishCancelable(savingNotification))
|
||||
{
|
||||
scope.Complete();
|
||||
return;
|
||||
@@ -344,8 +350,7 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
|
||||
if (raiseEvents)
|
||||
{
|
||||
saveEventArgs.CanCancel = false;
|
||||
scope.Events.Dispatch(SavedUser, this, saveEventArgs);
|
||||
scope.Notifications.Publish(new UserSavedNotification(entitiesA, evtMsgs).WithStateFrom(savingNotification));
|
||||
}
|
||||
|
||||
//commit the whole lot in one go
|
||||
@@ -709,14 +714,16 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
if (entityIds.Length == 0)
|
||||
return;
|
||||
|
||||
var evtMsgs = EventMessagesFactory.Get();
|
||||
|
||||
using (var scope = ScopeProvider.CreateScope())
|
||||
{
|
||||
_userGroupRepository.ReplaceGroupPermissions(groupId, permissions, entityIds);
|
||||
scope.Complete();
|
||||
|
||||
var assigned = permissions.Select(p => p.ToString(CultureInfo.InvariantCulture)).ToArray();
|
||||
scope.Events.Dispatch(UserGroupPermissionsAssigned, this,
|
||||
new SaveEventArgs<EntityPermission>(entityIds.Select(x => new EntityPermission(groupId, x, assigned)).ToArray(), false));
|
||||
var entityPermissions = entityIds.Select(x => new EntityPermission(groupId, x, assigned)).ToArray();
|
||||
scope.Notifications.Publish(new AssignedUserGroupPermissionsNotification(entityPermissions, evtMsgs));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -731,14 +738,16 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
if (entityIds.Length == 0)
|
||||
return;
|
||||
|
||||
var evtMsgs = EventMessagesFactory.Get();
|
||||
|
||||
using (var scope = ScopeProvider.CreateScope())
|
||||
{
|
||||
_userGroupRepository.AssignGroupPermission(groupId, permission, entityIds);
|
||||
scope.Complete();
|
||||
|
||||
var assigned = new[] { permission.ToString(CultureInfo.InvariantCulture) };
|
||||
scope.Events.Dispatch(UserGroupPermissionsAssigned, this,
|
||||
new SaveEventArgs<EntityPermission>(entityIds.Select(x => new EntityPermission(groupId, x, assigned)).ToArray(), false));
|
||||
var entityPermissions = entityIds.Select(x => new EntityPermission(groupId, x, assigned)).ToArray();
|
||||
scope.Notifications.Publish(new AssignedUserGroupPermissionsNotification(entityPermissions, evtMsgs));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -809,6 +818,8 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
/// Default is <c>True</c> otherwise set to <c>False</c> to not raise events</param>
|
||||
public void Save(IUserGroup userGroup, int[] userIds = null, bool raiseEvents = true)
|
||||
{
|
||||
var evtMsgs = EventMessagesFactory.Get();
|
||||
|
||||
using (var scope = ScopeProvider.CreateScope())
|
||||
{
|
||||
// we need to figure out which users have been added / removed, for audit purposes
|
||||
@@ -826,9 +837,19 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
removedUsers = groupIds.Except(userIds).Select(x => xGroupUsers[x]).Where(x => x.Id != 0).ToArray();
|
||||
}
|
||||
|
||||
var saveEventArgs = new SaveEventArgs<UserGroupWithUsers>(new UserGroupWithUsers(userGroup, addedUsers, removedUsers));
|
||||
var userGroupWithUsers = new UserGroupWithUsers(userGroup, addedUsers, removedUsers);
|
||||
|
||||
if (raiseEvents && scope.Events.DispatchCancelable(SavingUserGroup, this, saveEventArgs))
|
||||
// this is the default/expected notification for the IUserGroup entity being saved
|
||||
var savingNotification = new UserGroupSavingNotification(userGroup, evtMsgs);
|
||||
if (raiseEvents && scope.Notifications.PublishCancelable(savingNotification))
|
||||
{
|
||||
scope.Complete();
|
||||
return;
|
||||
}
|
||||
|
||||
// this is an additional notification for special auditing
|
||||
var savingUserGroupWithUsersNotification = new UserGroupWithUsersSavingNotification(userGroupWithUsers, evtMsgs);
|
||||
if (raiseEvents && scope.Notifications.PublishCancelable(savingUserGroupWithUsersNotification))
|
||||
{
|
||||
scope.Complete();
|
||||
return;
|
||||
@@ -838,8 +859,8 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
|
||||
if (raiseEvents)
|
||||
{
|
||||
saveEventArgs.CanCancel = false;
|
||||
scope.Events.Dispatch(SavedUserGroup, this, saveEventArgs);
|
||||
scope.Notifications.Publish(new UserGroupSavedNotification(userGroup, evtMsgs).WithStateFrom(savingNotification));
|
||||
scope.Notifications.Publish(new UserGroupWithUsersSavedNotification(userGroupWithUsers, evtMsgs).WithStateFrom(savingUserGroupWithUsersNotification));
|
||||
}
|
||||
|
||||
scope.Complete();
|
||||
@@ -852,10 +873,12 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
/// <param name="userGroup">UserGroup to delete</param>
|
||||
public void DeleteUserGroup(IUserGroup userGroup)
|
||||
{
|
||||
var evtMsgs = EventMessagesFactory.Get();
|
||||
|
||||
using (var scope = ScopeProvider.CreateScope())
|
||||
{
|
||||
var deleteEventArgs = new DeleteEventArgs<IUserGroup>(userGroup);
|
||||
if (scope.Events.DispatchCancelable(DeletingUserGroup, this, deleteEventArgs))
|
||||
var deletingNotification = new UserGroupDeletingNotification(userGroup, evtMsgs);
|
||||
if (scope.Notifications.PublishCancelable(deletingNotification))
|
||||
{
|
||||
scope.Complete();
|
||||
return;
|
||||
@@ -863,8 +886,7 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
|
||||
_userGroupRepository.Delete(userGroup);
|
||||
|
||||
deleteEventArgs.CanCancel = false;
|
||||
scope.Events.Dispatch(DeletedUserGroup, this, deleteEventArgs);
|
||||
scope.Notifications.Publish(new UserGroupDeletedNotification(userGroup, evtMsgs).WithStateFrom(deletingNotification));
|
||||
|
||||
scope.Complete();
|
||||
}
|
||||
@@ -1144,49 +1166,5 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Occurs before Save
|
||||
/// </summary>
|
||||
public static event TypedEventHandler<IUserService, SaveEventArgs<IUser>> SavingUser;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs after Save
|
||||
/// </summary>
|
||||
public static event TypedEventHandler<IUserService, SaveEventArgs<IUser>> SavedUser;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs before Delete
|
||||
/// </summary>
|
||||
public static event TypedEventHandler<IUserService, DeleteEventArgs<IUser>> DeletingUser;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs after Delete
|
||||
/// </summary>
|
||||
public static event TypedEventHandler<IUserService, DeleteEventArgs<IUser>> DeletedUser;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs before Save
|
||||
/// </summary>
|
||||
public static event TypedEventHandler<IUserService, SaveEventArgs<UserGroupWithUsers>> SavingUserGroup;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs after Save
|
||||
/// </summary>
|
||||
public static event TypedEventHandler<IUserService, SaveEventArgs<UserGroupWithUsers>> SavedUserGroup;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs before Delete
|
||||
/// </summary>
|
||||
public static event TypedEventHandler<IUserService, DeleteEventArgs<IUserGroup>> DeletingUserGroup;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs after Delete
|
||||
/// </summary>
|
||||
public static event TypedEventHandler<IUserService, DeleteEventArgs<IUserGroup>> DeletedUserGroup;
|
||||
|
||||
// TODO: still don't know if we need this yet unless we start caching permissions, but that also means we'll need another
|
||||
// event on the ContentService since there's a method there to modify node permissions too, or we can proxy events if needed.
|
||||
public static event TypedEventHandler<IUserService, SaveEventArgs<EntityPermission>> UserGroupPermissionsAssigned;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
namespace Umbraco.Cms.Infrastructure.Services.Notifications
|
||||
{
|
||||
public class AssignedMemberRolesNotification : MemberRolesNotification
|
||||
{
|
||||
public AssignedMemberRolesNotification(int[] memberIds, string[] roles) : base(memberIds, roles)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
using System.Collections.Generic;
|
||||
using Umbraco.Cms.Core.Events;
|
||||
using Umbraco.Cms.Core.Models.Membership;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Services.Notifications
|
||||
{
|
||||
public class AssignedUserGroupPermissionsNotification : EnumerableObjectNotification<EntityPermission>
|
||||
{
|
||||
public AssignedUserGroupPermissionsNotification(IEnumerable<EntityPermission> target, EventMessages messages) : base(target, messages)
|
||||
{
|
||||
}
|
||||
|
||||
public IEnumerable<EntityPermission> EntityPermissions => Target;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
// Copyright (c) Umbraco.
|
||||
// See LICENSE for more details.
|
||||
|
||||
using Umbraco.Cms.Core.Events;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Services.Notifications
|
||||
{
|
||||
public class DictionaryItemDeletedNotification : DeletedNotification<IDictionaryItem>
|
||||
{
|
||||
public DictionaryItemDeletedNotification(IDictionaryItem target, EventMessages messages) : base(target, messages)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
// Copyright (c) Umbraco.
|
||||
// See LICENSE for more details.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using Umbraco.Cms.Core.Events;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Services.Notifications
|
||||
{
|
||||
public class DictionaryItemDeletingNotification : DeletingNotification<IDictionaryItem>
|
||||
{
|
||||
public DictionaryItemDeletingNotification(IDictionaryItem target, EventMessages messages) : base(target, messages)
|
||||
{
|
||||
}
|
||||
|
||||
public DictionaryItemDeletingNotification(IEnumerable<IDictionaryItem> target, EventMessages messages) : base(target, messages)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
// Copyright (c) Umbraco.
|
||||
// See LICENSE for more details.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using Umbraco.Cms.Core.Events;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Services.Notifications
|
||||
{
|
||||
public class DictionaryItemSavedNotification : SavedNotification<IDictionaryItem>
|
||||
{
|
||||
public DictionaryItemSavedNotification(IDictionaryItem target, EventMessages messages) : base(target, messages)
|
||||
{
|
||||
}
|
||||
|
||||
public DictionaryItemSavedNotification(IEnumerable<IDictionaryItem> target, EventMessages messages) : base(target, messages)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
// Copyright (c) Umbraco.
|
||||
// See LICENSE for more details.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using Umbraco.Cms.Core.Events;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Services.Notifications
|
||||
{
|
||||
public class DictionaryItemSavingNotification : SavingNotification<IDictionaryItem>
|
||||
{
|
||||
public DictionaryItemSavingNotification(IDictionaryItem target, EventMessages messages) : base(target, messages)
|
||||
{
|
||||
}
|
||||
|
||||
public DictionaryItemSavingNotification(IEnumerable<IDictionaryItem> target, EventMessages messages) : base(target, messages)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
using Umbraco.Cms.Core.Events;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Models.Membership;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Services.Notifications
|
||||
{
|
||||
public class ExportedMemberNotification : INotification
|
||||
{
|
||||
public ExportedMemberNotification(IMember member, MemberExportModel exported)
|
||||
{
|
||||
Member = member;
|
||||
Exported = exported;
|
||||
}
|
||||
|
||||
public IMember Member { get; }
|
||||
|
||||
public MemberExportModel Exported { get; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
// Copyright (c) Umbraco.
|
||||
// See LICENSE for more details.
|
||||
|
||||
using Umbraco.Cms.Core.Events;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Services.Notifications
|
||||
{
|
||||
public class LanguageDeletedNotification : DeletedNotification<ILanguage>
|
||||
{
|
||||
public LanguageDeletedNotification(ILanguage target, EventMessages messages) : base(target, messages)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
// Copyright (c) Umbraco.
|
||||
// See LICENSE for more details.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using Umbraco.Cms.Core.Events;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Services.Notifications
|
||||
{
|
||||
public class LanguageDeletingNotification : DeletingNotification<ILanguage>
|
||||
{
|
||||
public LanguageDeletingNotification(ILanguage target, EventMessages messages) : base(target, messages)
|
||||
{
|
||||
}
|
||||
|
||||
public LanguageDeletingNotification(IEnumerable<ILanguage> target, EventMessages messages) : base(target, messages)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
// Copyright (c) Umbraco.
|
||||
// See LICENSE for more details.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using Umbraco.Cms.Core.Events;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Services.Notifications
|
||||
{
|
||||
public class LanguageSavedNotification : SavedNotification<ILanguage>
|
||||
{
|
||||
public LanguageSavedNotification(ILanguage target, EventMessages messages) : base(target, messages)
|
||||
{
|
||||
}
|
||||
|
||||
public LanguageSavedNotification(IEnumerable<ILanguage> target, EventMessages messages) : base(target, messages)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
// Copyright (c) Umbraco.
|
||||
// See LICENSE for more details.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using Umbraco.Cms.Core.Events;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Services.Notifications
|
||||
{
|
||||
public class LanguageSavingNotification : SavingNotification<ILanguage>
|
||||
{
|
||||
public LanguageSavingNotification(ILanguage target, EventMessages messages) : base(target, messages)
|
||||
{
|
||||
}
|
||||
|
||||
public LanguageSavingNotification(IEnumerable<ILanguage> target, EventMessages messages) : base(target, messages)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
// Copyright (c) Umbraco.
|
||||
// See LICENSE for more details.
|
||||
|
||||
using Umbraco.Cms.Core.Events;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Services.Notifications
|
||||
{
|
||||
public sealed class MemberDeletedNotification : DeletedNotification<IMember>
|
||||
{
|
||||
public MemberDeletedNotification(IMember target, EventMessages messages) : base(target, messages)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
// Copyright (c) Umbraco.
|
||||
// See LICENSE for more details.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using Umbraco.Cms.Core.Events;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Services.Notifications
|
||||
{
|
||||
public sealed class MemberDeletingNotification : DeletingNotification<IMember>
|
||||
{
|
||||
public MemberDeletingNotification(IMember target, EventMessages messages) : base(target, messages)
|
||||
{
|
||||
}
|
||||
|
||||
public MemberDeletingNotification(IEnumerable<IMember> target, EventMessages messages) : base(target, messages)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
using Umbraco.Cms.Core.Events;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Services.Notifications
|
||||
{
|
||||
public class MemberGroupDeletedNotification : DeletedNotification<IMemberGroup>
|
||||
{
|
||||
public MemberGroupDeletedNotification(IMemberGroup target, EventMessages messages) : base(target, messages)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
using System.Collections.Generic;
|
||||
using Umbraco.Cms.Core.Events;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Services.Notifications
|
||||
{
|
||||
public class MemberGroupDeletingNotification : DeletingNotification<IMemberGroup>
|
||||
{
|
||||
public MemberGroupDeletingNotification(IMemberGroup target, EventMessages messages) : base(target, messages)
|
||||
{
|
||||
}
|
||||
|
||||
public MemberGroupDeletingNotification(IEnumerable<IMemberGroup> target, EventMessages messages) : base(target, messages)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
using System.Collections.Generic;
|
||||
using Umbraco.Cms.Core.Events;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Services.Notifications
|
||||
{
|
||||
public class MemberGroupSavedNotification : SavedNotification<IMemberGroup>
|
||||
{
|
||||
public MemberGroupSavedNotification(IMemberGroup target, EventMessages messages) : base(target, messages)
|
||||
{
|
||||
}
|
||||
|
||||
public MemberGroupSavedNotification(IEnumerable<IMemberGroup> target, EventMessages messages) : base(target, messages)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
using System.Collections.Generic;
|
||||
using Umbraco.Cms.Core.Events;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Services.Notifications
|
||||
{
|
||||
public class MemberGroupSavingNotification : SavingNotification<IMemberGroup>
|
||||
{
|
||||
public MemberGroupSavingNotification(IMemberGroup target, EventMessages messages) : base(target, messages)
|
||||
{
|
||||
}
|
||||
|
||||
public MemberGroupSavingNotification(IEnumerable<IMemberGroup> target, EventMessages messages) : base(target, messages)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
using Umbraco.Cms.Core.Events;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Services.Notifications
|
||||
{
|
||||
public abstract class MemberRolesNotification : INotification
|
||||
{
|
||||
protected MemberRolesNotification(int[] memberIds, string[] roles)
|
||||
{
|
||||
MemberIds = memberIds;
|
||||
Roles = roles;
|
||||
}
|
||||
|
||||
public int[] MemberIds { get; }
|
||||
|
||||
public string[] Roles { get; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
// Copyright (c) Umbraco.
|
||||
// See LICENSE for more details.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using Umbraco.Cms.Core.Events;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Services.Notifications
|
||||
{
|
||||
public sealed class MemberSavedNotification : SavedNotification<IMember>
|
||||
{
|
||||
public MemberSavedNotification(IMember target, EventMessages messages) : base(target, messages)
|
||||
{
|
||||
}
|
||||
|
||||
public MemberSavedNotification(IEnumerable<IMember> target, EventMessages messages) : base(target, messages)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
// Copyright (c) Umbraco.
|
||||
// See LICENSE for more details.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using Umbraco.Cms.Core.Events;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Services.Notifications
|
||||
{
|
||||
public sealed class MemberSavingNotification : SavingNotification<IMember>
|
||||
{
|
||||
public MemberSavingNotification(IMember target, EventMessages messages) : base(target, messages)
|
||||
{
|
||||
}
|
||||
|
||||
public MemberSavingNotification(IEnumerable<IMember> target, EventMessages messages) : base(target, messages)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
// Copyright (c) Umbraco.
|
||||
// See LICENSE for more details.
|
||||
|
||||
using Umbraco.Cms.Core.Events;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Services.Notifications
|
||||
{
|
||||
public sealed class PublicAccessEntryDeletedNotification : DeletedNotification<PublicAccessEntry>
|
||||
{
|
||||
public PublicAccessEntryDeletedNotification(PublicAccessEntry target, EventMessages messages) : base(target, messages)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
// Copyright (c) Umbraco.
|
||||
// See LICENSE for more details.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using Umbraco.Cms.Core.Events;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Services.Notifications
|
||||
{
|
||||
public sealed class PublicAccessEntryDeletingNotification : DeletingNotification<PublicAccessEntry>
|
||||
{
|
||||
public PublicAccessEntryDeletingNotification(PublicAccessEntry target, EventMessages messages) : base(target, messages)
|
||||
{
|
||||
}
|
||||
|
||||
public PublicAccessEntryDeletingNotification(IEnumerable<PublicAccessEntry> target, EventMessages messages) : base(target, messages)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
// Copyright (c) Umbraco.
|
||||
// See LICENSE for more details.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using Umbraco.Cms.Core.Events;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Services.Notifications
|
||||
{
|
||||
public sealed class PublicAccessEntrySavedNotification : SavedNotification<PublicAccessEntry>
|
||||
{
|
||||
public PublicAccessEntrySavedNotification(PublicAccessEntry target, EventMessages messages) : base(target, messages)
|
||||
{
|
||||
}
|
||||
|
||||
public PublicAccessEntrySavedNotification(IEnumerable<PublicAccessEntry> target, EventMessages messages) : base(target, messages)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
// Copyright (c) Umbraco.
|
||||
// See LICENSE for more details.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using Umbraco.Cms.Core.Events;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Services.Notifications
|
||||
{
|
||||
public sealed class PublicAccessEntrySavingNotification : SavingNotification<PublicAccessEntry>
|
||||
{
|
||||
public PublicAccessEntrySavingNotification(PublicAccessEntry target, EventMessages messages) : base(target, messages)
|
||||
{
|
||||
}
|
||||
|
||||
public PublicAccessEntrySavingNotification(IEnumerable<PublicAccessEntry> target, EventMessages messages) : base(target, messages)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
namespace Umbraco.Cms.Infrastructure.Services.Notifications
|
||||
{
|
||||
public class RemovedMemberRolesNotification : MemberRolesNotification
|
||||
{
|
||||
public RemovedMemberRolesNotification(int[] memberIds, string[] roles) : base(memberIds, roles)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
// Copyright (c) Umbraco.
|
||||
// See LICENSE for more details.
|
||||
|
||||
using Umbraco.Cms.Core.Events;
|
||||
using Umbraco.Cms.Core.Models.Membership;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Services.Notifications
|
||||
{
|
||||
public sealed class UserDeletedNotification : DeletedNotification<IUser>
|
||||
{
|
||||
public UserDeletedNotification(IUser target, EventMessages messages) : base(target, messages)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
// Copyright (c) Umbraco.
|
||||
// See LICENSE for more details.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using Umbraco.Cms.Core.Events;
|
||||
using Umbraco.Cms.Core.Models.Membership;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Services.Notifications
|
||||
{
|
||||
public sealed class UserDeletingNotification : DeletingNotification<IUser>
|
||||
{
|
||||
public UserDeletingNotification(IUser target, EventMessages messages) : base(target, messages)
|
||||
{
|
||||
}
|
||||
|
||||
public UserDeletingNotification(IEnumerable<IUser> target, EventMessages messages) : base(target, messages)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
// Copyright (c) Umbraco.
|
||||
// See LICENSE for more details.
|
||||
|
||||
using Umbraco.Cms.Core.Events;
|
||||
using Umbraco.Cms.Core.Models.Membership;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Services.Notifications
|
||||
{
|
||||
public sealed class UserGroupDeletedNotification : DeletedNotification<IUserGroup>
|
||||
{
|
||||
public UserGroupDeletedNotification(IUserGroup target, EventMessages messages) : base(target, messages)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
// Copyright (c) Umbraco.
|
||||
// See LICENSE for more details.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using Umbraco.Cms.Core.Events;
|
||||
using Umbraco.Cms.Core.Models.Membership;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Services.Notifications
|
||||
{
|
||||
public sealed class UserGroupDeletingNotification : DeletingNotification<IUserGroup>
|
||||
{
|
||||
public UserGroupDeletingNotification(IUserGroup target, EventMessages messages) : base(target, messages)
|
||||
{
|
||||
}
|
||||
|
||||
public UserGroupDeletingNotification(IEnumerable<IUserGroup> target, EventMessages messages) : base(target, messages)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
// Copyright (c) Umbraco.
|
||||
// See LICENSE for more details.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using Umbraco.Cms.Core.Events;
|
||||
using Umbraco.Cms.Core.Models.Membership;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Services.Notifications
|
||||
{
|
||||
public sealed class UserGroupSavedNotification : SavedNotification<IUserGroup>
|
||||
{
|
||||
public UserGroupSavedNotification(IUserGroup target, EventMessages messages) : base(target, messages)
|
||||
{
|
||||
}
|
||||
|
||||
public UserGroupSavedNotification(IEnumerable<IUserGroup> target, EventMessages messages) : base(target, messages)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
// Copyright (c) Umbraco.
|
||||
// See LICENSE for more details.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using Umbraco.Cms.Core.Events;
|
||||
using Umbraco.Cms.Core.Models.Membership;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Services.Notifications
|
||||
{
|
||||
public sealed class UserGroupSavingNotification : SavingNotification<IUserGroup>
|
||||
{
|
||||
public UserGroupSavingNotification(IUserGroup target, EventMessages messages) : base(target, messages)
|
||||
{
|
||||
}
|
||||
|
||||
public UserGroupSavingNotification(IEnumerable<IUserGroup> target, EventMessages messages) : base(target, messages)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
// Copyright (c) Umbraco.
|
||||
// See LICENSE for more details.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using Umbraco.Cms.Core.Events;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Services.Notifications
|
||||
{
|
||||
public sealed class UserGroupWithUsersSavedNotification : SavedNotification<UserGroupWithUsers>
|
||||
{
|
||||
public UserGroupWithUsersSavedNotification(UserGroupWithUsers target, EventMessages messages) : base(target, messages)
|
||||
{
|
||||
}
|
||||
|
||||
public UserGroupWithUsersSavedNotification(IEnumerable<UserGroupWithUsers> target, EventMessages messages) : base(target, messages)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
// Copyright (c) Umbraco.
|
||||
// See LICENSE for more details.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using Umbraco.Cms.Core.Events;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Services.Notifications
|
||||
{
|
||||
public sealed class UserGroupWithUsersSavingNotification : SavingNotification<UserGroupWithUsers>
|
||||
{
|
||||
public UserGroupWithUsersSavingNotification(UserGroupWithUsers target, EventMessages messages) : base(target, messages)
|
||||
{
|
||||
}
|
||||
|
||||
public UserGroupWithUsersSavingNotification(IEnumerable<UserGroupWithUsers> target, EventMessages messages) : base(target, messages)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
// Copyright (c) Umbraco.
|
||||
// See LICENSE for more details.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using Umbraco.Cms.Core.Events;
|
||||
using Umbraco.Cms.Core.Models.Membership;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Services.Notifications
|
||||
{
|
||||
public sealed class UserSavedNotification : SavedNotification<IUser>
|
||||
{
|
||||
public UserSavedNotification(IUser target, EventMessages messages) : base(target, messages)
|
||||
{
|
||||
}
|
||||
|
||||
public UserSavedNotification(IEnumerable<IUser> target, EventMessages messages) : base(target, messages)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
// Copyright (c) Umbraco.
|
||||
// See LICENSE for more details.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using Umbraco.Cms.Core.Events;
|
||||
using Umbraco.Cms.Core.Models.Membership;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Services.Notifications
|
||||
{
|
||||
public sealed class UserSavingNotification : SavingNotification<IUser>
|
||||
{
|
||||
public UserSavingNotification(IUser target, EventMessages messages) : base(target, messages)
|
||||
{
|
||||
}
|
||||
|
||||
public UserSavingNotification(IEnumerable<IUser> target, EventMessages messages) : base(target, messages)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -101,6 +101,17 @@
|
||||
<AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleTo">
|
||||
<_Parameter1>DynamicProxyGenAssembly2</_Parameter1>
|
||||
</AssemblyAttribute>
|
||||
|
||||
<!-- Making internals visible to Umbraco Forms -->
|
||||
<AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleTo">
|
||||
<_Parameter1>Umbraco.Forms.Core</_Parameter1>
|
||||
</AssemblyAttribute>
|
||||
<AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleTo">
|
||||
<_Parameter1>Umbraco.Forms.Core.Providers</_Parameter1>
|
||||
</AssemblyAttribute>
|
||||
<AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleTo">
|
||||
<_Parameter1>Umbraco.Forms.Web</_Parameter1>
|
||||
</AssemblyAttribute>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
using Umbraco.Cms.Core.Composing;
|
||||
using Umbraco.Cms.Core.DependencyInjection;
|
||||
using Umbraco.Cms.Infrastructure.Services.Notifications;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.PublishedCache.Compose
|
||||
{
|
||||
public sealed class NotificationsComposer : ICoreComposer
|
||||
{
|
||||
public void Compose(IUmbracoBuilder builder) =>
|
||||
builder.AddNotificationHandler<LanguageSavedNotification, PublishedSnapshotServiceEventHandler>();
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,7 @@ using Umbraco.Cms.Core.Services.Changes;
|
||||
using Umbraco.Cms.Core.Services.Implement;
|
||||
using Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement;
|
||||
using Umbraco.Cms.Infrastructure.PublishedCache.Persistence;
|
||||
using Umbraco.Cms.Infrastructure.Services.Notifications;
|
||||
using Umbraco.Extensions;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.PublishedCache
|
||||
@@ -16,7 +17,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache
|
||||
/// <summary>
|
||||
/// Subscribes to Umbraco events to ensure nucache remains consistent with the source data
|
||||
/// </summary>
|
||||
public class PublishedSnapshotServiceEventHandler : IDisposable
|
||||
public class PublishedSnapshotServiceEventHandler : IDisposable, INotificationHandler<LanguageSavedNotification>
|
||||
{
|
||||
private readonly IRuntimeState _runtime;
|
||||
private bool _disposedValue;
|
||||
@@ -79,9 +80,6 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache
|
||||
ContentTypeService.ScopedRefreshedEntity += OnContentTypeRefreshedEntity;
|
||||
MediaTypeService.ScopedRefreshedEntity += OnMediaTypeRefreshedEntity;
|
||||
MemberTypeService.ScopedRefreshedEntity += OnMemberTypeRefreshedEntity;
|
||||
|
||||
// TODO: This should be a cache refresher call!
|
||||
LocalizationService.SavedLanguage += OnLanguageSaved;
|
||||
}
|
||||
|
||||
private void TearDownRepositoryEvents()
|
||||
@@ -95,7 +93,6 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache
|
||||
ContentTypeService.ScopedRefreshedEntity -= OnContentTypeRefreshedEntity;
|
||||
MediaTypeService.ScopedRefreshedEntity -= OnMediaTypeRefreshedEntity;
|
||||
MemberTypeService.ScopedRefreshedEntity -= OnMemberTypeRefreshedEntity;
|
||||
LocalizationService.SavedLanguage -= OnLanguageSaved; // TODO: Shouldn't this be a cache refresher event?
|
||||
}
|
||||
|
||||
// note: if the service is not ready, ie _isReady is false, then we still handle repository events,
|
||||
@@ -156,13 +153,14 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: This should be a cache refresher call!
|
||||
/// <summary>
|
||||
/// If a <see cref="ILanguage"/> is ever saved with a different culture, we need to rebuild all of the content nucache database table
|
||||
/// </summary>
|
||||
private void OnLanguageSaved(ILocalizationService sender, SaveEventArgs<ILanguage> e)
|
||||
public void Handle(LanguageSavedNotification notification)
|
||||
{
|
||||
// culture changed on an existing language
|
||||
var cultureChanged = e.SavedEntities.Any(x => !x.WasPropertyDirty(nameof(ILanguage.Id)) && x.WasPropertyDirty(nameof(ILanguage.IsoCode)));
|
||||
var cultureChanged = notification.SavedEntities.Any(x => !x.WasPropertyDirty(nameof(ILanguage.Id)) && x.WasPropertyDirty(nameof(ILanguage.IsoCode)));
|
||||
if (cultureChanged)
|
||||
{
|
||||
// Rebuild all content for all content types
|
||||
|
||||
@@ -17,6 +17,13 @@ namespace Umbraco.Cms.Tests.Common.Builders.Extensions
|
||||
return builder;
|
||||
}
|
||||
|
||||
public static T WithId<T, TId>(this T builder, TId id)
|
||||
where T : IWithIdBuilder<TId>
|
||||
{
|
||||
builder.Id = id;
|
||||
return builder;
|
||||
}
|
||||
|
||||
public static T WithoutIdentity<T>(this T builder)
|
||||
where T : IWithIdBuilder
|
||||
{
|
||||
|
||||
@@ -7,4 +7,9 @@ namespace Umbraco.Cms.Tests.Common.Builders.Interfaces
|
||||
{
|
||||
int? Id { get; set; }
|
||||
}
|
||||
|
||||
public interface IWithIdBuilder<TId>
|
||||
{
|
||||
TId Id { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
// Copyright (c) Umbraco.
|
||||
// See LICENSE for more details.
|
||||
|
||||
using Umbraco.Cms.Core.Models.Identity;
|
||||
using Umbraco.Cms.Tests.Common.Builders.Interfaces;
|
||||
|
||||
namespace Umbraco.Cms.Tests.Common.Builders
|
||||
{
|
||||
public class UmbracoIdentityRoleBuilder : BuilderBase<UmbracoIdentityRole>,
|
||||
IWithIdBuilder<string>,
|
||||
IWithNameBuilder
|
||||
{
|
||||
private string _id;
|
||||
private string _name;
|
||||
|
||||
public UmbracoIdentityRoleBuilder WithTestName(string id)
|
||||
{
|
||||
_name = "testname";
|
||||
_id = id;
|
||||
return this;
|
||||
}
|
||||
|
||||
string IWithNameBuilder.Name
|
||||
{
|
||||
get => _name;
|
||||
set => _name = value;
|
||||
}
|
||||
|
||||
string IWithIdBuilder<string>.Id
|
||||
{
|
||||
get => _id;
|
||||
set => _id = value;
|
||||
}
|
||||
|
||||
public override UmbracoIdentityRole Build()
|
||||
{
|
||||
var id = _id;
|
||||
var name = _name;
|
||||
|
||||
return new UmbracoIdentityRole
|
||||
{
|
||||
Id = id,
|
||||
Name = name,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Umbraco.Cms.Core.Configuration.Models;
|
||||
using Umbraco.Cms.Core.Models.Identity;
|
||||
using Umbraco.Cms.Core.Models.Membership;
|
||||
using Umbraco.Cms.Tests.Common.Builders.Extensions;
|
||||
using Umbraco.Cms.Tests.Common.Builders.Interfaces;
|
||||
@@ -12,6 +13,7 @@ using Umbraco.Extensions;
|
||||
|
||||
namespace Umbraco.Cms.Tests.Common.Builders
|
||||
{
|
||||
|
||||
public class UserBuilder : UserBuilder<object>
|
||||
{
|
||||
public UserBuilder()
|
||||
|
||||
@@ -42,26 +42,12 @@ namespace Umbraco.Cms.Tests.Integration.Cache
|
||||
|
||||
var definitions = new IEventDefinition[]
|
||||
{
|
||||
new EventDefinition<IUserService, SaveEventArgs<IUser>>(null, UserService, new SaveEventArgs<IUser>(Enumerable.Empty<IUser>())),
|
||||
new EventDefinition<IUserService, DeleteEventArgs<IUser>>(null, UserService, new DeleteEventArgs<IUser>(Enumerable.Empty<IUser>())),
|
||||
new EventDefinition<IUserService, SaveEventArgs<UserGroupWithUsers>>(null, UserService, new SaveEventArgs<UserGroupWithUsers>(Enumerable.Empty<UserGroupWithUsers>())),
|
||||
new EventDefinition<IUserService, DeleteEventArgs<IUserGroup>>(null, UserService, new DeleteEventArgs<IUserGroup>(Enumerable.Empty<IUserGroup>())),
|
||||
|
||||
new EventDefinition<ILocalizationService, SaveEventArgs<IDictionaryItem>>(null, LocalizationService, new SaveEventArgs<IDictionaryItem>(Enumerable.Empty<IDictionaryItem>())),
|
||||
new EventDefinition<ILocalizationService, DeleteEventArgs<IDictionaryItem>>(null, LocalizationService, new DeleteEventArgs<IDictionaryItem>(Enumerable.Empty<IDictionaryItem>())),
|
||||
|
||||
new EventDefinition<IDataTypeService, SaveEventArgs<IDataType>>(null, DataTypeService, new SaveEventArgs<IDataType>(Enumerable.Empty<IDataType>())),
|
||||
new EventDefinition<IDataTypeService, DeleteEventArgs<IDataType>>(null, DataTypeService, new DeleteEventArgs<IDataType>(Enumerable.Empty<IDataType>())),
|
||||
|
||||
new EventDefinition<IFileService, SaveEventArgs<IStylesheet>>(null, FileService, new SaveEventArgs<IStylesheet>(Enumerable.Empty<IStylesheet>())),
|
||||
new EventDefinition<IFileService, DeleteEventArgs<IStylesheet>>(null, FileService, new DeleteEventArgs<IStylesheet>(Enumerable.Empty<IStylesheet>())),
|
||||
|
||||
new EventDefinition<IDomainService, SaveEventArgs<IDomain>>(null, DomainService, new SaveEventArgs<IDomain>(Enumerable.Empty<IDomain>())),
|
||||
new EventDefinition<IDomainService, DeleteEventArgs<IDomain>>(null, DomainService, new DeleteEventArgs<IDomain>(Enumerable.Empty<IDomain>())),
|
||||
|
||||
new EventDefinition<ILocalizationService, SaveEventArgs<ILanguage>>(null, LocalizationService, new SaveEventArgs<ILanguage>(Enumerable.Empty<ILanguage>())),
|
||||
new EventDefinition<ILocalizationService, DeleteEventArgs<ILanguage>>(null, LocalizationService, new DeleteEventArgs<ILanguage>(Enumerable.Empty<ILanguage>())),
|
||||
|
||||
new EventDefinition<IContentTypeService, SaveEventArgs<IContentType>>(null, ContentTypeService, new SaveEventArgs<IContentType>(Enumerable.Empty<IContentType>())),
|
||||
new EventDefinition<IContentTypeService, DeleteEventArgs<IContentType>>(null, ContentTypeService, new DeleteEventArgs<IContentType>(Enumerable.Empty<IContentType>())),
|
||||
new EventDefinition<IMediaTypeService, SaveEventArgs<IMediaType>>(null, MediaTypeService, new SaveEventArgs<IMediaType>(Enumerable.Empty<IMediaType>())),
|
||||
@@ -76,19 +62,10 @@ namespace Umbraco.Cms.Tests.Integration.Cache
|
||||
new EventDefinition<IMacroService, SaveEventArgs<IMacro>>(null, MacroService, new SaveEventArgs<IMacro>(Enumerable.Empty<IMacro>())),
|
||||
new EventDefinition<IMacroService, DeleteEventArgs<IMacro>>(null, MacroService, new DeleteEventArgs<IMacro>(Enumerable.Empty<IMacro>())),
|
||||
|
||||
new EventDefinition<IMemberService, SaveEventArgs<IMember>>(null, MemberService, new SaveEventArgs<IMember>(Enumerable.Empty<IMember>())),
|
||||
new EventDefinition<IMemberService, DeleteEventArgs<IMember>>(null, MemberService, new DeleteEventArgs<IMember>(Enumerable.Empty<IMember>())),
|
||||
|
||||
new EventDefinition<IMemberGroupService, SaveEventArgs<IMemberGroup>>(null, MemberGroupService, new SaveEventArgs<IMemberGroup>(Enumerable.Empty<IMemberGroup>())),
|
||||
new EventDefinition<IMemberGroupService, DeleteEventArgs<IMemberGroup>>(null, MemberGroupService, new DeleteEventArgs<IMemberGroup>(Enumerable.Empty<IMemberGroup>())),
|
||||
|
||||
// not managed
|
||||
//new EventDefinition<IContentService, SaveEventArgs<IContent>>(null, ContentService, new SaveEventArgs<IContent>(Enumerable.Empty<IContent>()), "SavedBlueprint"),
|
||||
//new EventDefinition<IContentService, DeleteEventArgs<IContent>>(null, ContentService, new DeleteEventArgs<IContent>(Enumerable.Empty<IContent>()), "DeletedBlueprint"),
|
||||
|
||||
new EventDefinition<IPublicAccessService, SaveEventArgs<PublicAccessEntry>>(null, PublicAccessService, new SaveEventArgs<PublicAccessEntry>(Enumerable.Empty<PublicAccessEntry>())),
|
||||
new EventDefinition<IPublicAccessService, DeleteEventArgs<PublicAccessEntry>>(null, PublicAccessService, new DeleteEventArgs<PublicAccessEntry>(Enumerable.Empty<PublicAccessEntry>())),
|
||||
|
||||
new EventDefinition<IRelationService, SaveEventArgs<IRelationType>>(null, RelationService, new SaveEventArgs<IRelationType>(Enumerable.Empty<IRelationType>())),
|
||||
new EventDefinition<IRelationService, DeleteEventArgs<IRelationType>>(null, RelationService, new DeleteEventArgs<IRelationType>(Enumerable.Empty<IRelationType>())),
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
// Copyright (c) Umbraco.
|
||||
// Copyright (c) Umbraco.
|
||||
// See LICENSE for more details.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using NUnit.Framework;
|
||||
using Umbraco.Cms.Core.Cache;
|
||||
using Umbraco.Cms.Core.DependencyInjection;
|
||||
using Umbraco.Cms.Core.Events;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Models.Membership;
|
||||
@@ -13,10 +13,12 @@ using Umbraco.Cms.Core.Scoping;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.Core.Services.Implement;
|
||||
using Umbraco.Cms.Core.Sync;
|
||||
using Umbraco.Cms.Core.Web;
|
||||
using Umbraco.Cms.Infrastructure.PublishedCache;
|
||||
using Umbraco.Cms.Infrastructure.Services.Notifications;
|
||||
using Umbraco.Cms.Infrastructure.Sync;
|
||||
using Umbraco.Cms.Tests.Common.Testing;
|
||||
using Umbraco.Cms.Tests.Integration.Testing;
|
||||
using Umbraco.Extensions;
|
||||
|
||||
namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Scoping
|
||||
{
|
||||
@@ -44,6 +46,21 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Scoping
|
||||
return result;
|
||||
}
|
||||
|
||||
protected override void CustomTestSetup(IUmbracoBuilder builder)
|
||||
{
|
||||
builder.Services.AddUnique<IServerMessenger, LocalServerMessenger>();
|
||||
builder
|
||||
.AddNotificationHandler<DictionaryItemDeletedNotification, DistributedCacheBinder>()
|
||||
.AddNotificationHandler<DictionaryItemSavedNotification, DistributedCacheBinder>()
|
||||
.AddNotificationHandler<LanguageSavedNotification, DistributedCacheBinder>()
|
||||
.AddNotificationHandler<LanguageDeletedNotification, DistributedCacheBinder>()
|
||||
.AddNotificationHandler<UserSavedNotification, DistributedCacheBinder>()
|
||||
.AddNotificationHandler<LanguageDeletedNotification, DistributedCacheBinder>()
|
||||
.AddNotificationHandler<MemberGroupDeletedNotification, DistributedCacheBinder>()
|
||||
.AddNotificationHandler<MemberGroupSavedNotification, DistributedCacheBinder>();
|
||||
builder.AddNotificationHandler<LanguageSavedNotification, PublishedSnapshotServiceEventHandler>();
|
||||
}
|
||||
|
||||
[TearDown]
|
||||
public void Teardown()
|
||||
{
|
||||
@@ -61,18 +78,18 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Scoping
|
||||
var user = (IUser)new User(GlobalSettings, "name", "email", "username", "rawPassword");
|
||||
service.Save(user);
|
||||
|
||||
// global cache contains the entity
|
||||
// User has been saved so the cache has been cleared of it
|
||||
var globalCached = (IUser)globalCache.Get(GetCacheIdKey<IUser>(user.Id), () => null);
|
||||
Assert.IsNull(globalCached);
|
||||
// Get user again to load it into the cache again, this also ensure we don't modify the one that's in the cache.
|
||||
user = service.GetUserById(user.Id);
|
||||
|
||||
// global cache contains the entity
|
||||
globalCached = (IUser)globalCache.Get(GetCacheIdKey<IUser>(user.Id), () => null);
|
||||
Assert.IsNotNull(globalCached);
|
||||
Assert.AreEqual(user.Id, globalCached.Id);
|
||||
Assert.AreEqual("name", globalCached.Name);
|
||||
|
||||
// get user again - else we'd modify the one that's in the cache
|
||||
user = service.GetUserById(user.Id);
|
||||
|
||||
_distributedCacheBinder = new DistributedCacheBinder(new DistributedCache(ServerMessenger, CacheRefresherCollection), GetRequiredService<IUmbracoContextFactory>(), GetRequiredService<ILogger<DistributedCacheBinder>>());
|
||||
_distributedCacheBinder.BindEvents(true);
|
||||
|
||||
Assert.IsNull(scopeProvider.AmbientScope);
|
||||
using (IScope scope = scopeProvider.CreateScope(repositoryCacheMode: RepositoryCacheMode.Scoped))
|
||||
{
|
||||
@@ -154,9 +171,6 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Scoping
|
||||
Assert.AreEqual(lang.Id, globalCached.Id);
|
||||
Assert.AreEqual("fr-FR", globalCached.IsoCode);
|
||||
|
||||
_distributedCacheBinder = new DistributedCacheBinder(new DistributedCache(ServerMessenger, CacheRefresherCollection), GetRequiredService<IUmbracoContextFactory>(), GetRequiredService<ILogger<DistributedCacheBinder>>());
|
||||
_distributedCacheBinder.BindEvents(true);
|
||||
|
||||
Assert.IsNull(scopeProvider.AmbientScope);
|
||||
using (IScope scope = scopeProvider.CreateScope(repositoryCacheMode: RepositoryCacheMode.Scoped))
|
||||
{
|
||||
@@ -244,15 +258,16 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Scoping
|
||||
};
|
||||
service.Save(item);
|
||||
|
||||
// Refresh the cache manually because we can't unbind
|
||||
service.GetDictionaryItemById(item.Id);
|
||||
service.GetLanguageById(lang.Id);
|
||||
|
||||
// global cache contains the entity
|
||||
var globalCached = (IDictionaryItem)globalCache.Get(GetCacheIdKey<IDictionaryItem>(item.Id), () => null);
|
||||
Assert.IsNotNull(globalCached);
|
||||
Assert.AreEqual(item.Id, globalCached.Id);
|
||||
Assert.AreEqual("item-key", globalCached.ItemKey);
|
||||
|
||||
_distributedCacheBinder = new DistributedCacheBinder(new DistributedCache(ServerMessenger, CacheRefresherCollection), GetRequiredService<IUmbracoContextFactory>(), GetRequiredService<ILogger<DistributedCacheBinder>>());
|
||||
_distributedCacheBinder.BindEvents(true);
|
||||
|
||||
Assert.IsNull(scopeProvider.AmbientScope);
|
||||
using (IScope scope = scopeProvider.CreateScope(repositoryCacheMode: RepositoryCacheMode.Scoped))
|
||||
{
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user