diff --git a/src/Umbraco.Core/Composing/CompositionRoots/CoreModelMappersCompositionRoot.cs b/src/Umbraco.Core/Composing/CompositionRoots/CoreMappingProfilesCompositionRoot.cs similarity index 59% rename from src/Umbraco.Core/Composing/CompositionRoots/CoreModelMappersCompositionRoot.cs rename to src/Umbraco.Core/Composing/CompositionRoots/CoreMappingProfilesCompositionRoot.cs index 9e1054d3c8..9c2ed4781b 100644 --- a/src/Umbraco.Core/Composing/CompositionRoots/CoreModelMappersCompositionRoot.cs +++ b/src/Umbraco.Core/Composing/CompositionRoots/CoreMappingProfilesCompositionRoot.cs @@ -3,11 +3,11 @@ using Umbraco.Core.Models.Identity; namespace Umbraco.Core.Composing.CompositionRoots { - public sealed class CoreModelMappersCompositionRoot : ICompositionRoot + public sealed class CoreMappingProfilesCompositionRoot : ICompositionRoot { public void Compose(IServiceRegistry container) { - container.Register(); + container.Register(); } } } \ No newline at end of file diff --git a/src/Umbraco.Core/CoreRuntimeComponent.cs b/src/Umbraco.Core/CoreRuntimeComponent.cs index cc44f16954..074f961f1e 100644 --- a/src/Umbraco.Core/CoreRuntimeComponent.cs +++ b/src/Umbraco.Core/CoreRuntimeComponent.cs @@ -13,7 +13,6 @@ using Umbraco.Core.Composing.CompositionRoots; using Umbraco.Core.IO; using Umbraco.Core.Logging; using Umbraco.Core.Manifest; -using Umbraco.Core.Models.Mapping; using Umbraco.Core.Models.PublishedContent; using Umbraco.Core.Persistence; using Umbraco.Core.Persistence.Migrations; @@ -37,7 +36,7 @@ namespace Umbraco.Core composition.Container.RegisterFrom(); composition.Container.RegisterFrom(); composition.Container.RegisterFrom(); - composition.Container.RegisterFrom(); + composition.Container.RegisterFrom(); // register database builder // *not* a singleton, don't want to keep it around @@ -124,17 +123,17 @@ namespace Umbraco.Core composition.Container.RegisterSingleton(); } - internal void Initialize(IEnumerable modelMapperConfigurations) + internal void Initialize(IEnumerable mapperProfiles) { //TODO: Remove these for v8! LegacyPropertyEditorIdToAliasConverter.CreateMappingsForCoreEditors(); LegacyParameterEditorAliasConverter.CreateMappingsForCoreEditors(); - // model mapper configurations have been registered & are created by the container + // mapper profiles have been registered & are created by the container Mapper.Initialize(configuration => { - foreach (var m in modelMapperConfigurations) - m.ConfigureMappings(configuration); + foreach (var profile in mapperProfiles) + configuration.AddProfile(profile); }); // ensure we have some essential directories diff --git a/src/Umbraco.Core/Models/Identity/IdentityModelMappings.cs b/src/Umbraco.Core/Models/Identity/IdentityModelMappings.cs deleted file mode 100644 index ead07ce86d..0000000000 --- a/src/Umbraco.Core/Models/Identity/IdentityModelMappings.cs +++ /dev/null @@ -1,64 +0,0 @@ -using System; -using System.Linq; -using AutoMapper; -using Umbraco.Core.Models.Mapping; -using Umbraco.Core.Models.Membership; -using Umbraco.Core.Security; -using Umbraco.Core.Services; - -namespace Umbraco.Core.Models.Identity -{ - public class IdentityModelMappings : ModelMapperConfiguration - { - private readonly ILocalizedTextService _textService; - - public IdentityModelMappings(ILocalizedTextService textService) - { - _textService = textService; - } - - public override void ConfigureMappings(IMapperConfiguration config) - { - config.CreateMap() - .ForMember(user => user.LastLoginDateUtc, expression => expression.MapFrom(user => user.LastLoginDate.ToUniversalTime())) - .ForMember(user => user.Email, expression => expression.MapFrom(user => user.Email)) - .ForMember(user => user.Id, expression => expression.MapFrom(user => user.Id)) - .ForMember(user => user.LockoutEndDateUtc, expression => expression.MapFrom(user => user.IsLockedOut ? DateTime.MaxValue.ToUniversalTime() : (DateTime?) null)) - .ForMember(user => user.UserName, expression => expression.MapFrom(user => user.Username)) - .ForMember(user => user.PasswordHash, expression => expression.MapFrom(user => GetPasswordHash(user.RawPasswordValue))) - .ForMember(user => user.Culture, expression => expression.MapFrom(user => user.GetUserCulture(_textService))) - .ForMember(user => user.Name, expression => expression.MapFrom(user => user.Name)) - .ForMember(user => user.StartMediaId, expression => expression.MapFrom(user => user.StartMediaId)) - .ForMember(user => user.StartContentId, expression => expression.MapFrom(user => user.StartContentId)) - .ForMember(user => user.UserTypeAlias, expression => expression.MapFrom(user => user.UserType.Alias)) - .ForMember(user => user.AccessFailedCount, expression => expression.MapFrom(user => user.FailedPasswordAttempts)) - .ForMember(user => user.AllowedSections, expression => expression.MapFrom(user => user.AllowedSections.ToArray())) - .ForMember(user => user.LockoutEnabled, expression => expression.Ignore()) - .ForMember(user => user.Logins, expression => expression.Ignore()) - .ForMember(user => user.LoginsChanged, expression => expression.Ignore()) - .ForMember(user => user.EmailConfirmed, expression => expression.Ignore()) - .ForMember(user => user.PhoneNumber, expression => expression.Ignore()) - .ForMember(user => user.PhoneNumberConfirmed, expression => expression.Ignore()) - .ForMember(user => user.TwoFactorEnabled, expression => expression.Ignore()) - .ForMember(user => user.Roles, expression => expression.Ignore()) - .ForMember(user => user.Claims, expression => expression.Ignore()); - - config.CreateMap() - .ConstructUsing((BackOfficeIdentityUser user) => new UserData(Guid.NewGuid().ToString("N"))) //this is the 'session id' - .ForMember(detail => detail.Id, opt => opt.MapFrom(user => user.Id)) - .ForMember(detail => detail.AllowedApplications, opt => opt.MapFrom(user => user.AllowedSections)) - .ForMember(detail => detail.RealName, opt => opt.MapFrom(user => user.Name)) - .ForMember(detail => detail.Roles, opt => opt.MapFrom(user => new[] { user.UserTypeAlias })) - .ForMember(detail => detail.StartContentNode, opt => opt.MapFrom(user => user.StartContentId)) - .ForMember(detail => detail.StartMediaNode, opt => opt.MapFrom(user => user.StartMediaId)) - .ForMember(detail => detail.Username, opt => opt.MapFrom(user => user.UserName)) - .ForMember(detail => detail.Culture, opt => opt.MapFrom(user => user.Culture)) - .ForMember(detail => detail.SessionId, opt => opt.MapFrom(user => user.SecurityStamp.IsNullOrWhiteSpace() ? Guid.NewGuid().ToString("N") : user.SecurityStamp)); - } - - private string GetPasswordHash(string storedPass) - { - return storedPass.StartsWith("___UIDEMPTYPWORD__") ? null : storedPass; - } - } -} \ No newline at end of file diff --git a/src/Umbraco.Core/Models/Identity/IdentityProfile.cs b/src/Umbraco.Core/Models/Identity/IdentityProfile.cs new file mode 100644 index 0000000000..ebeb77c4ea --- /dev/null +++ b/src/Umbraco.Core/Models/Identity/IdentityProfile.cs @@ -0,0 +1,56 @@ +using System; +using System.Linq; +using AutoMapper; +using Umbraco.Core.Models.Membership; +using Umbraco.Core.Security; +using Umbraco.Core.Services; + +namespace Umbraco.Core.Models.Identity +{ + public class IdentityProfile : Profile + { + public IdentityProfile(ILocalizedTextService textService) + { + CreateMap() + .ForMember(dest => dest.LastLoginDateUtc, opt => opt.MapFrom(src => src.LastLoginDate.ToUniversalTime())) + .ForMember(dest => dest.Email, opt => opt.MapFrom(src => src.Email)) + .ForMember(dest => dest.Id, opt => opt.MapFrom(src => src.Id)) + .ForMember(dest => dest.LockoutEndDateUtc, opt => opt.MapFrom(src => src.IsLockedOut ? DateTime.MaxValue.ToUniversalTime() : (DateTime?) null)) + .ForMember(dest => dest.UserName, opt => opt.MapFrom(src => src.Username)) + .ForMember(dest => dest.PasswordHash, opt => opt.MapFrom(user => GetPasswordHash(user.RawPasswordValue))) + .ForMember(dest => dest.Culture, opt => opt.MapFrom(src => src.GetUserCulture(textService))) + .ForMember(dest => dest.Name, opt => opt.MapFrom(src => src.Name)) + .ForMember(dest => dest.StartMediaId, opt => opt.MapFrom(src => src.StartMediaId)) + .ForMember(dest => dest.StartContentId, opt => opt.MapFrom(src => src.StartContentId)) + .ForMember(dest => dest.UserTypeAlias, opt => opt.MapFrom(src => src.UserType.Alias)) + .ForMember(dest => dest.AccessFailedCount, opt => opt.MapFrom(src => src.FailedPasswordAttempts)) + .ForMember(dest => dest.AllowedSections, opt => opt.MapFrom(src => src.AllowedSections.ToArray())) + .ForMember(dest => dest.LockoutEnabled, opt => opt.Ignore()) + .ForMember(dest => dest.Logins, opt => opt.Ignore()) + .ForMember(dest => dest.LoginsChanged, opt => opt.Ignore()) + .ForMember(dest => dest.EmailConfirmed, opt => opt.Ignore()) + .ForMember(dest => dest.PhoneNumber, opt => opt.Ignore()) + .ForMember(dest => dest.PhoneNumberConfirmed, opt => opt.Ignore()) + .ForMember(dest => dest.TwoFactorEnabled, opt => opt.Ignore()) + .ForMember(dest => dest.Roles, opt => opt.Ignore()) + .ForMember(dest => dest.Claims, opt => opt.Ignore()); + + CreateMap() + .ConstructUsing(source => new UserData(Guid.NewGuid().ToString("N"))) //this is the 'session id' + .ForMember(dest => dest.Id, opt => opt.MapFrom(src => src.Id)) + .ForMember(dest => dest.AllowedApplications, opt => opt.MapFrom(src => src.AllowedSections)) + .ForMember(dest => dest.RealName, opt => opt.MapFrom(src => src.Name)) + .ForMember(dest => dest.Roles, opt => opt.MapFrom(user => new[] { user.UserTypeAlias })) + .ForMember(dest => dest.StartContentNode, opt => opt.MapFrom(src => src.StartContentId)) + .ForMember(dest => dest.StartMediaNode, opt => opt.MapFrom(src => src.StartMediaId)) + .ForMember(dest => dest.Username, opt => opt.MapFrom(src => src.UserName)) + .ForMember(dest => dest.Culture, opt => opt.MapFrom(src => src.Culture)) + .ForMember(dest => dest.SessionId, opt => opt.MapFrom(src => src.SecurityStamp.IsNullOrWhiteSpace() ? Guid.NewGuid().ToString("N") : src.SecurityStamp)); + } + + private static string GetPasswordHash(string storedPass) + { + return storedPass.StartsWith("___UIDEMPTYPWORD__") ? null : storedPass; + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Models/Mapping/ModelMapperConfiguration.cs b/src/Umbraco.Core/Models/Mapping/ModelMapperConfiguration.cs deleted file mode 100644 index 8768776281..0000000000 --- a/src/Umbraco.Core/Models/Mapping/ModelMapperConfiguration.cs +++ /dev/null @@ -1,12 +0,0 @@ -using AutoMapper; - -namespace Umbraco.Core.Models.Mapping -{ - /// - /// Used to declare a mapper configuration - /// - public abstract class ModelMapperConfiguration - { - public abstract void ConfigureMappings(IMapperConfiguration config); - } -} \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Factories/ModelFactoryConfiguration.cs b/src/Umbraco.Core/Persistence/Factories/ModelFactoryConfiguration.cs index 2aadb46084..c003b26a98 100644 --- a/src/Umbraco.Core/Persistence/Factories/ModelFactoryConfiguration.cs +++ b/src/Umbraco.Core/Persistence/Factories/ModelFactoryConfiguration.cs @@ -1,6 +1,6 @@ using AutoMapper; -using Umbraco.Core.Models.Mapping; +// fixme what is this?! namespace Umbraco.Core.Persistence.Factories { diff --git a/src/Umbraco.Core/Persistence/Factories/TaskFactory.cs b/src/Umbraco.Core/Persistence/Factories/TaskFactory.cs index d60403abea..6a8292579d 100644 --- a/src/Umbraco.Core/Persistence/Factories/TaskFactory.cs +++ b/src/Umbraco.Core/Persistence/Factories/TaskFactory.cs @@ -1,6 +1,4 @@ -using AutoMapper; using Umbraco.Core.Models; -using Umbraco.Core.Models.Mapping; using Umbraco.Core.Models.Rdbms; namespace Umbraco.Core.Persistence.Factories diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 3e0650b1d1..78ddc5d584 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -53,7 +53,7 @@ - + @@ -152,7 +152,7 @@ - + @@ -530,7 +530,7 @@ - + @@ -562,7 +562,6 @@ - diff --git a/src/Umbraco.Tests/Models/Mapping/AutoMapper6Tests.cs b/src/Umbraco.Tests/Models/Mapping/AutoMapper6Tests.cs new file mode 100644 index 0000000000..59c514f654 --- /dev/null +++ b/src/Umbraco.Tests/Models/Mapping/AutoMapper6Tests.cs @@ -0,0 +1,198 @@ +using System; +using AutoMapper; +using NUnit.Framework; + +namespace Umbraco.Tests.Models.Mapping +{ + [TestFixture] + public class AutoMapper6Tests + { + [Test] + public void Test1() + { + ThingProfile.CtorCount = 0; + Assert.AreEqual(0, ThingProfile.CtorCount); + + var config = new MapperConfiguration(cfg => + { + cfg.AddProfile(); + }); + + Assert.AreEqual(1, ThingProfile.CtorCount); + Assert.AreEqual(0, MemberValueResolver.CtorCount); + + var mapper = config.CreateMapper(); + + Assert.AreEqual(1, ThingProfile.CtorCount); + Assert.AreEqual(0, MemberValueResolver.CtorCount); + + var thingA = new ThingA { ValueInt = 42, ValueString = "foo" }; + var thingB = mapper.Map(thingA); + Assert.AreEqual(42, thingB.ValueInt); + Assert.AreEqual("!!foo!!", thingB.ValueString); + + mapper.Map(thingA); + mapper.Map(thingA); + mapper.Map(thingA); + Assert.AreEqual(1, ThingProfile.CtorCount); // one single profile + Assert.AreEqual(4, MemberValueResolver.CtorCount); // many resolvers + } + + [Test] + public void Test2() + { + var config = new MapperConfiguration(cfg => + { + cfg.AddProfile(); + }); + + var mapper = config.CreateMapper(); + + Assert.AreEqual(0, ValueResolver.CtorCount); + + var thingA = new ThingA { ValueInt = 42, ValueString = "foo" }; + var thingB = mapper.Map(thingA); + Assert.AreEqual(42, thingB.ValueInt); + Assert.AreEqual("!!foo!!", thingB.ValueString); + + mapper.Map(thingA); + mapper.Map(thingA); + mapper.Map(thingA); + Assert.AreEqual(4, ValueResolver.CtorCount); // many resolvers + } + + [Test] + public void Test3() + { + var config = new MapperConfiguration(cfg => + { + cfg.AddProfile(); + }); + + var mapper = config.CreateMapper(); + + var thingA = new ThingA { ValueInt = 42, ValueString = "foo" }; + var thingB = mapper.Map(thingA); + Assert.AreEqual(42, thingB.ValueInt); + Assert.AreEqual("!!foo!!", thingB.ValueString); + + mapper.Map(thingA); + mapper.Map(thingA); + mapper.Map(thingA); + } + + // Resolve destination member using a custom value resolver + // void ResolveUsing() + // where TValueResolver : IValueResolver; + + // Resolve destination member using a custom value resolver from a source member + // void ResolveUsing(Expression> sourceMember) + // where TValueResolver : IMemberValueResolver; + // void ResolveUsing(string sourceMemberName) + // where TValueResolver : IMemberValueResolver; + + // Resolve destination member using a custom value resolver instance + // void ResolveUsing(IValueResolver valueResolver); + // void ResolveUsing(IMemberValueResolver valueResolver, Expression> sourceMember); + + // Resolve destination member using a custom value resolver callback + // void ResolveUsing(Func resolver); + // void ResolveUsing(Func resolver); + // void ResolveUsing(Func resolver); + // void ResolveUsing(Func resolver); + + // read https://stackoverflow.com/questions/14875075/automapper-what-is-the-difference-between-mapfrom-and-resolveusing + // about the diff between MapFrom and ResolveUsing... keeping ResolveUsing in our code + + public class ThingProfile : Profile + { + public static int CtorCount { get; set; } + + public ThingProfile(int ver) + { + CtorCount++; + + var map = CreateMap() + .ForMember(dest => dest.ValueInt, opt => opt.MapFrom(src => src.ValueInt)); + + switch (ver) + { + case 0: + break; + case 1: + map + .ForMember(dest => dest.ValueString, opt => opt.ResolveUsing(src => src.ValueString)); + break; + case 2: + map + .ForMember(dest => dest.ValueString, opt => opt.ResolveUsing()); + break; + case 3: + // in most cases that should be perfectly enough? + map + .ForMember(dest => dest.ValueString, opt => opt.ResolveUsing(source => "!!" + source.ValueString + "!!")); + break; + default: + throw new ArgumentOutOfRangeException(nameof(ver)); + } + } + } + + public class ThingProfile1 : ThingProfile + { + public ThingProfile1() : base(1) { } + } + + public class ThingProfile2 : ThingProfile + { + public ThingProfile2() : base(2) { } + } + + public class ThingProfile3 : ThingProfile + { + public ThingProfile3() : base(3) { } + } + + public class ValueResolver : IValueResolver + { + public static int CtorCount { get; set; } + + public ValueResolver() + { + CtorCount++; + } + + public string Resolve(ThingA source, ThingB destination, string destMember, ResolutionContext context) + { + return "!!" + source.ValueString + "!!"; + } + } + + public class MemberValueResolver : IMemberValueResolver + { + public static int CtorCount { get; set; } + + public MemberValueResolver() + { + CtorCount++; + } + + public string Resolve(ThingA source, ThingB destination, string sourceMember, string destMember, ResolutionContext context) + { + return "!!" + sourceMember + "!!"; + } + } + + public class ThingA + { + public int ValueInt { get; set; } + public string ValueString { get; set; } + } + + public class ThingB + { + public int ValueInt { get; set; } + public string ValueString { get; set; } + } + } +} diff --git a/src/Umbraco.Tests/Models/Mapping/AutoMapperTests.cs b/src/Umbraco.Tests/Models/Mapping/AutoMapperTests.cs index 6c78cff778..2470eee7e8 100644 --- a/src/Umbraco.Tests/Models/Mapping/AutoMapperTests.cs +++ b/src/Umbraco.Tests/Models/Mapping/AutoMapperTests.cs @@ -33,8 +33,50 @@ namespace Umbraco.Tests.Models.Mapping } [Test] - public void Assert_Valid_Mappings() + public void AssertConfigurationIsValid() { + var profiles = Container.GetAllInstances().ToArray(); + + var config = new MapperConfiguration(cfg => + { + foreach (var profile in profiles) + cfg.AddProfile(profile); + }); + + // validate each profile (better granularity for error reports) + + foreach (var profile in profiles) + { + try + { + config.AssertConfigurationIsValid(profile.GetType().FullName); + Console.WriteLine("OK " + profile.GetType().FullName); + } + catch + { + Console.WriteLine("KO " + profile.GetType().FullName); + } + } + + Console.WriteLine(); + + foreach (var profile in profiles) + { + try + { + config.AssertConfigurationIsValid(profile.GetType().FullName); + } + catch + { + Console.WriteLine("KO " + profile.GetType().FullName); + throw; + } + } + + // validate the global config + config.AssertConfigurationIsValid(); + + // validate the static config (should be the same) Mapper.AssertConfigurationIsValid(); } } diff --git a/src/Umbraco.Tests/Models/Mapping/ContentTypeModelMappingTests.cs b/src/Umbraco.Tests/Models/Mapping/ContentTypeModelMappingTests.cs index 3baa7be101..78922603ce 100644 --- a/src/Umbraco.Tests/Models/Mapping/ContentTypeModelMappingTests.cs +++ b/src/Umbraco.Tests/Models/Mapping/ContentTypeModelMappingTests.cs @@ -59,10 +59,10 @@ namespace Umbraco.Tests.Models.Mapping Mapper.Initialize(configuration => { //initialize our content type mapper - var mapper = new ContentTypeModelMapper(_editorsMock.Object, _dataTypeService.Object, _fileService.Object, _contentTypeService.Object, Mock.Of()); - mapper.ConfigureMappings(configuration); - var entityMapper = new EntityModelMapper(); - entityMapper.ConfigureMappings(configuration); + var profile1 = new ContentTypeProfile(_editorsMock.Object, _dataTypeService.Object, _fileService.Object, _contentTypeService.Object, Mock.Of()); + configuration.AddProfile(profile1); + var profile2 = new EntityProfile(); + configuration.AddProfile(profile2); }); } diff --git a/src/Umbraco.Tests/Testing/UmbracoTestBase.cs b/src/Umbraco.Tests/Testing/UmbracoTestBase.cs index 885f63a8a8..398afaee5e 100644 --- a/src/Umbraco.Tests/Testing/UmbracoTestBase.cs +++ b/src/Umbraco.Tests/Testing/UmbracoTestBase.cs @@ -15,7 +15,6 @@ using Umbraco.Core.Events; using Umbraco.Core.IO; using Umbraco.Core.Logging; using Umbraco.Core.Manifest; -using Umbraco.Core.Models.Mapping; using Umbraco.Core.Models.PublishedContent; using Umbraco.Core.Persistence; using Umbraco.Core.Persistence.Mappers; @@ -185,8 +184,8 @@ namespace Umbraco.Tests.Testing { if (configure == false) return; - Container.RegisterFrom(); - Container.RegisterFrom(); + Container.RegisterFrom(); + Container.RegisterFrom(); } protected virtual void ComposePluginManager(UmbracoTestOptions.PluginManager pluginManager) @@ -313,9 +312,9 @@ namespace Umbraco.Tests.Testing Mapper.Initialize(configuration => { - var mappers = Container.GetAllInstances(); - foreach (var mapper in mappers) - mapper.ConfigureMappings(configuration); + var profiles = Container.GetAllInstances(); + foreach (var profile in profiles) + configuration.AddProfile(profile); }); } diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index 34698cab58..f17d933649 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -51,8 +51,8 @@ false - - ..\packages\AutoMapper.4.2.1\lib\net45\AutoMapper.dll + + ..\packages\AutoMapper.6.1.1\lib\net45\AutoMapper.dll ..\packages\Castle.Core.4.1.1\lib\net45\Castle.Core.dll @@ -208,6 +208,7 @@ + diff --git a/src/Umbraco.Tests/packages.config b/src/Umbraco.Tests/packages.config index 6e6b78aed0..8e9ac2499e 100644 --- a/src/Umbraco.Tests/packages.config +++ b/src/Umbraco.Tests/packages.config @@ -1,6 +1,6 @@  - + diff --git a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj index ed8318bfbf..76e4a1a669 100644 --- a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj +++ b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj @@ -48,6 +48,9 @@ ..\bin\Release\ + + ..\packages\AutoMapper.6.1.1\lib\net45\AutoMapper.dll + ..\packages\ImageProcessor.Web.4.8.4\lib\net45\ImageProcessor.Web.dll @@ -265,10 +268,6 @@ ../packages/Microsoft.AspNet.WebApi.Core.5.2.3/lib/net45/System.Web.Http.dll True - - ../packages/AutoMapper.4.2.1/lib/net45/AutoMapper.dll - True - ../packages/ClientDependency-Mvc5.1.8.0.0/lib/net45/ClientDependency.Core.Mvc.dll True diff --git a/src/Umbraco.Web.UI/packages.config b/src/Umbraco.Web.UI/packages.config index 350608dd55..622c240ba5 100644 --- a/src/Umbraco.Web.UI/packages.config +++ b/src/Umbraco.Web.UI/packages.config @@ -1,6 +1,6 @@  - + diff --git a/src/Umbraco.Web/DI/WebMappingProfilesCompositionRoot.cs b/src/Umbraco.Web/DI/WebMappingProfilesCompositionRoot.cs new file mode 100644 index 0000000000..2a8daad829 --- /dev/null +++ b/src/Umbraco.Web/DI/WebMappingProfilesCompositionRoot.cs @@ -0,0 +1,26 @@ +using LightInject; +using Umbraco.Web.Models.Mapping; + +namespace Umbraco.Web.DI +{ + public sealed class WebMappingProfilesCompositionRoot : ICompositionRoot + { + public void Compose(IServiceRegistry container) + { + container.Register(); + container.Register(); + container.Register(); + container.Register(); + container.Register(); + container.Register(); + container.Register(); + container.Register(); + container.Register(); + container.Register(); + container.Register(); + container.Register(); + container.Register(); + container.Register(); + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Web/DI/WebModelMappersCompositionRoot.cs b/src/Umbraco.Web/DI/WebModelMappersCompositionRoot.cs deleted file mode 100644 index c5031fdd7d..0000000000 --- a/src/Umbraco.Web/DI/WebModelMappersCompositionRoot.cs +++ /dev/null @@ -1,26 +0,0 @@ -using LightInject; -using Umbraco.Web.Models.Mapping; - -namespace Umbraco.Web.DI -{ - public sealed class WebModelMappersCompositionRoot : ICompositionRoot - { - public void Compose(IServiceRegistry container) - { - container.Register(); - container.Register(); - container.Register(); - container.Register(); - container.Register(); - container.Register(); - container.Register(); - container.Register(); - container.Register(); - container.Register(); - container.Register(); - container.Register(); - container.Register(); - container.Register(); - } - } -} \ No newline at end of file diff --git a/src/Umbraco.Web/Models/Mapping/ActionButtonsResolver.cs b/src/Umbraco.Web/Models/Mapping/ActionButtonsResolver.cs new file mode 100644 index 0000000000..3d970b3406 --- /dev/null +++ b/src/Umbraco.Web/Models/Mapping/ActionButtonsResolver.cs @@ -0,0 +1,46 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Umbraco.Core.Models; +using Umbraco.Core.Services; + +namespace Umbraco.Web.Models.Mapping +{ + /// + //TODO: This is horribly inneficient + /// Creates the list of action buttons allowed for this user - Publish, Send to publish, save, unpublish returned as the button's 'letter' + /// + internal class ActionButtonsResolver + { + private readonly Lazy _userService; + + public ActionButtonsResolver(Lazy userService) + { + _userService = userService; + } + + public IEnumerable Resolve(IContent source) + { + if (UmbracoContext.Current == null) + { + //cannot check permissions without a context + return Enumerable.Empty(); + } + var svc = _userService.Value; + + var permissions = svc.GetPermissions( + //TODO: This is certainly not ideal usage here - perhaps the best way to deal with this in the future is + // with the IUmbracoContextAccessor. In the meantime, if used outside of a web app this will throw a null + // refrence exception :( + UmbracoContext.Current.Security.CurrentUser, + // Here we need to do a special check since this could be new content, in which case we need to get the permissions + // from the parent, not the existing one otherwise permissions would be coming from the root since Id is 0. + source.HasIdentity ? source.Id : source.ParentId) + .FirstOrDefault(); + + return permissions == null + ? Enumerable.Empty() + : permissions.AssignedPermissions.Where(x => x.Length == 1).Select(x => x.ToUpperInvariant()[0]); + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Web/Models/Mapping/AvailablePropertyEditorsResolver.cs b/src/Umbraco.Web/Models/Mapping/AvailablePropertyEditorsResolver.cs index d26c88129d..bbd772c62c 100644 --- a/src/Umbraco.Web/Models/Mapping/AvailablePropertyEditorsResolver.cs +++ b/src/Umbraco.Web/Models/Mapping/AvailablePropertyEditorsResolver.cs @@ -8,7 +8,7 @@ using Umbraco.Web.Models.ContentEditing; namespace Umbraco.Web.Models.Mapping { - internal class AvailablePropertyEditorsResolver : ValueResolver> + internal class AvailablePropertyEditorsResolver { private readonly IContentSection _contentSection; @@ -17,7 +17,7 @@ namespace Umbraco.Web.Models.Mapping _contentSection = contentSection; } - protected override IEnumerable ResolveCore(IDataTypeDefinition source) + public IEnumerable Resolve(IDataTypeDefinition source) { return Current.PropertyEditors .Where(x => diff --git a/src/Umbraco.Web/Models/Mapping/CodeFileDisplayMapper.cs b/src/Umbraco.Web/Models/Mapping/CodeFileProfile.cs similarity index 84% rename from src/Umbraco.Web/Models/Mapping/CodeFileDisplayMapper.cs rename to src/Umbraco.Web/Models/Mapping/CodeFileProfile.cs index 6a0d5c06e2..fbbdffc304 100644 --- a/src/Umbraco.Web/Models/Mapping/CodeFileDisplayMapper.cs +++ b/src/Umbraco.Web/Models/Mapping/CodeFileProfile.cs @@ -5,29 +5,28 @@ using System.Text; using System.Threading.Tasks; using AutoMapper; using Umbraco.Core; -using Umbraco.Core.Models.Mapping; using Umbraco.Core.Models; using Umbraco.Web.Models.ContentEditing; namespace Umbraco.Web.Models.Mapping { - public class CodeFileDisplayMapper : ModelMapperConfiguration + public class CodeFileProfile : Profile { - public override void ConfigureMappings(IMapperConfiguration config) + public CodeFileProfile() { - config.CreateMap() + CreateMap() .ForMember(x => x.FileType, exp => exp.Ignore()) .ForMember(x => x.Notifications, exp => exp.Ignore()) .ForMember(x => x.Path, exp => exp.Ignore()) .ForMember(x => x.Snippet, exp => exp.Ignore()); - config.CreateMap() + CreateMap() .ForMember(x => x.FileType, exp => exp.Ignore()) .ForMember(x => x.Notifications, exp => exp.Ignore()) .ForMember(x => x.Path, exp => exp.Ignore()) .ForMember(x => x.Snippet, exp => exp.Ignore()); - config.CreateMap() + CreateMap() .ForMember(x => x.DeletedDate, exp => exp.Ignore()) .ForMember(x => x.Id, exp => exp.Ignore()) .ForMember(x => x.Key, exp => exp.Ignore()) @@ -40,7 +39,7 @@ namespace Umbraco.Web.Models.Mapping .ForMember(x => x.OriginalPath, exp => exp.Ignore()) .ForMember(x => x.HasIdentity, exp => exp.Ignore()); - config.CreateMap() + CreateMap() .ForMember(x => x.DeletedDate, exp => exp.Ignore()) .ForMember(x => x.Id, exp => exp.Ignore()) .ForMember(x => x.Key, exp => exp.Ignore()) diff --git a/src/Umbraco.Web/Models/Mapping/ContentModelMapper.cs b/src/Umbraco.Web/Models/Mapping/ContentProfile.cs similarity index 53% rename from src/Umbraco.Web/Models/Mapping/ContentModelMapper.cs rename to src/Umbraco.Web/Models/Mapping/ContentProfile.cs index 89aea7d099..a1b8888d2b 100644 --- a/src/Umbraco.Web/Models/Mapping/ContentModelMapper.cs +++ b/src/Umbraco.Web/Models/Mapping/ContentProfile.cs @@ -1,268 +1,216 @@ -using System; -using System.Collections.Generic; -using System.Globalization; -using System.Linq; -using System.Web; -using System.Web.Mvc; -using System.Web.Routing; -using AutoMapper; -using Umbraco.Core; -using Umbraco.Core.Models; -using Umbraco.Core.Models.Mapping; -using Umbraco.Core.Services; -using Umbraco.Web.Composing; -using Umbraco.Web.Models.ContentEditing; -using Umbraco.Web.Trees; -using Umbraco.Web.Routing; -using Umbraco.Web._Legacy.Actions; - -namespace Umbraco.Web.Models.Mapping -{ - /// - /// Declares how model mappings for content - /// - internal class ContentModelMapper : ModelMapperConfiguration - { - private readonly IUserService _userService; - private readonly ILocalizedTextService _textService; - private readonly IContentService _contentService; - private readonly IContentTypeService _contentTypeService; - private readonly IDataTypeService _dataTypeService; - - public ContentModelMapper(IUserService userService, ILocalizedTextService textService, IContentService contentService, IContentTypeService contentTypeService, IDataTypeService dataTypeService) - { - _userService = userService; - _textService = textService; - _contentService = contentService; - _contentTypeService = contentTypeService; - _dataTypeService = dataTypeService; - } - - public override void ConfigureMappings(IMapperConfiguration config) - { - - //FROM IContent TO ContentItemDisplay - config.CreateMap() - .ForMember(display => display.Udi, expression => expression.MapFrom(content => Udi.Create(Constants.UdiEntityType.Document, content.Key))) - .ForMember(display => display.Owner, expression => expression.ResolveUsing(new OwnerResolver(_userService))) - .ForMember(display => display.Updater, expression => expression.ResolveUsing(new CreatorResolver(_userService))) - .ForMember(display => display.Icon, expression => expression.MapFrom(content => content.ContentType.Icon)) - .ForMember(display => display.ContentTypeAlias, expression => expression.MapFrom(content => content.ContentType.Alias)) - .ForMember(display => display.ContentTypeName, expression => expression.MapFrom(content => content.ContentType.Name)) - .ForMember(display => display.IsContainer, expression => expression.MapFrom(content => content.ContentType.IsContainer)) - .ForMember(display => display.IsChildOfListView, expression => expression.Ignore()) - .ForMember(display => display.Trashed, expression => expression.MapFrom(content => content.Trashed)) - .ForMember(display => display.PublishDate, expression => expression.MapFrom(content => GetPublishedDate(content))) - .ForMember(display => display.TemplateAlias, expression => expression.MapFrom(content => content.Template.Alias)) - .ForMember(display => display.HasPublishedVersion, expression => expression.MapFrom(content => content.HasPublishedVersion)) - .ForMember(display => display.Urls, - expression => expression.MapFrom(content => - UmbracoContext.Current == null - ? new[] {"Cannot generate urls without a current Umbraco Context"} - : content.GetContentUrls(UmbracoContext.Current))) - .ForMember(display => display.Properties, expression => expression.Ignore()) - .ForMember(display => display.AllowPreview, expression => expression.Ignore()) - .ForMember(display => display.TreeNodeUrl, expression => expression.Ignore()) - .ForMember(display => display.Notifications, expression => expression.Ignore()) - .ForMember(display => display.Errors, expression => expression.Ignore()) - .ForMember(display => display.Alias, expression => expression.Ignore()) - .ForMember(display => display.Tabs, expression => expression.ResolveUsing(new TabsAndPropertiesResolver(_textService))) - .ForMember(display => display.AllowedActions, expression => expression.ResolveUsing( - new ActionButtonsResolver(new Lazy(() => _userService)))) - .AfterMap((content, display) => AfterMap(content, display, _dataTypeService, _textService, _contentTypeService, _contentService)); - - //FROM IContent TO ContentItemBasic - config.CreateMap>() - .ForMember(display => display.Udi, expression => expression.MapFrom(content => Udi.Create(Constants.UdiEntityType.Document, content.Key))) - .ForMember(dto => dto.Owner, expression => expression.ResolveUsing(new OwnerResolver(_userService))) - .ForMember(dto => dto.Updater, expression => expression.ResolveUsing(new CreatorResolver(_userService))) - .ForMember(dto => dto.Icon, expression => expression.MapFrom(content => content.ContentType.Icon)) - .ForMember(dto => dto.Trashed, expression => expression.MapFrom(content => content.Trashed)) - .ForMember(dto => dto.HasPublishedVersion, expression => expression.MapFrom(content => content.HasPublishedVersion)) - .ForMember(dto => dto.ContentTypeAlias, expression => expression.MapFrom(content => content.ContentType.Alias)) - .ForMember(dto => dto.Alias, expression => expression.Ignore()); - - //FROM IContent TO ContentItemDto - config.CreateMap>() - .ForMember(display => display.Udi, expression => expression.MapFrom(content => Udi.Create(Constants.UdiEntityType.Document, content.Key))) - .ForMember(dto => dto.Owner, expression => expression.ResolveUsing(new OwnerResolver(_userService))) - .ForMember(dto => dto.HasPublishedVersion, expression => expression.MapFrom(content => content.HasPublishedVersion)) - .ForMember(dto => dto.Updater, expression => expression.Ignore()) - .ForMember(dto => dto.Icon, expression => expression.Ignore()) - .ForMember(dto => dto.Alias, expression => expression.Ignore()); - } - - private static DateTime? GetPublishedDate(IContent content) - { - var date = ((Content) content).PublishedDate; - return date == default (DateTime) ? (DateTime?) null : date; - } - - /// - /// Maps the generic tab with custom properties for content - /// - /// - /// - /// - /// - /// - /// - private static void AfterMap(IContent content, ContentItemDisplay display, IDataTypeService dataTypeService, - ILocalizedTextService localizedText, IContentTypeService contentTypeService, IContentService contentService) - { - // map the IsChildOfListView (this is actually if it is a descendant of a list view!) - var parent = content.Parent(contentService); - display.IsChildOfListView = parent != null && (parent.ContentType.IsContainer || contentTypeService.HasContainerInPath(parent.Path)); - - //map the tree node url - if (HttpContext.Current != null) - { - var urlHelper = new UrlHelper(HttpContext.Current.Request.RequestContext); - var url = urlHelper.GetUmbracoApiService(controller => controller.GetTreeNode(display.Id.ToString(), null)); - display.TreeNodeUrl = url; - } - - //fill in the template config to be passed to the template drop down. - var templateItemConfig = new Dictionary {{"", "Choose..."}}; - foreach (var t in content.ContentType.AllowedTemplates - .Where(t => t.Alias.IsNullOrWhiteSpace() == false && t.Name.IsNullOrWhiteSpace() == false)) - { - templateItemConfig.Add(t.Alias, t.Name); - } - - if (content.ContentType.IsContainer) - { - TabsAndPropertiesResolver.AddListView(display, "content", dataTypeService, localizedText); - } - - var properties = new List - { - new ContentPropertyDisplay - { - Alias = string.Format("{0}doctype", Constants.PropertyEditors.InternalGenericPropertiesPrefix), - Label = localizedText.Localize("content/documentType"), - Value = localizedText.UmbracoDictionaryTranslate(display.ContentTypeName), - View = Current.PropertyEditors[Constants.PropertyEditors.NoEditAlias].ValueEditor.View - }, - new ContentPropertyDisplay - { - Alias = string.Format("{0}releasedate", Constants.PropertyEditors.InternalGenericPropertiesPrefix), - Label = localizedText.Localize("content/releaseDate"), - Value = display.ReleaseDate.HasValue ? display.ReleaseDate.Value.ToIsoString() : null, - //Not editible for people without publish permission (U4-287) - View = display.AllowedActions.Contains(ActionPublish.Instance.Letter) ? "datepicker" : Current.PropertyEditors[Constants.PropertyEditors.NoEditAlias].ValueEditor.View, - Config = new Dictionary - { - {"offsetTime", "1"} - } - //TODO: Fix up hard coded datepicker - }, - new ContentPropertyDisplay - { - Alias = string.Format("{0}expiredate", Constants.PropertyEditors.InternalGenericPropertiesPrefix), - Label = localizedText.Localize("content/unpublishDate"), - Value = display.ExpireDate.HasValue ? display.ExpireDate.Value.ToIsoString() : null, - //Not editible for people without publish permission (U4-287) - View = display.AllowedActions.Contains(ActionPublish.Instance.Letter) ? "datepicker" : Current.PropertyEditors[Constants.PropertyEditors.NoEditAlias].ValueEditor.View, - Config = new Dictionary - { - {"offsetTime", "1"} - } - //TODO: Fix up hard coded datepicker - }, - new ContentPropertyDisplay - { - Alias = string.Format("{0}template", Constants.PropertyEditors.InternalGenericPropertiesPrefix), - Label = localizedText.Localize("template/template"), - Value = display.TemplateAlias, - View = "dropdown", //TODO: Hard coding until we make a real dropdown property editor to lookup - Config = new Dictionary - { - {"items", templateItemConfig} - } - } - }; - - - TabsAndPropertiesResolver.MapGenericProperties(content, display, localizedText, properties.ToArray(), - genericProperties => - { - //TODO: This would be much nicer with the IUmbracoContextAccessor so we don't use singletons - //If this is a web request and there's a user signed in and the - // user has access to the settings section, we will - if (HttpContext.Current != null && UmbracoContext.Current != null && UmbracoContext.Current.Security.CurrentUser != null - && UmbracoContext.Current.Security.CurrentUser.AllowedSections.Any(x => x.Equals(Constants.Applications.Settings))) - { - var currentDocumentType = contentTypeService.Get(display.ContentTypeAlias); - var currentDocumentTypeName = currentDocumentType == null ? string.Empty : localizedText.UmbracoDictionaryTranslate(currentDocumentType.Name); - - var currentDocumentTypeId = currentDocumentType == null ? string.Empty : currentDocumentType.Id.ToString(CultureInfo.InvariantCulture); - //TODO: Hard coding this is not good - var docTypeLink = string.Format("#/settings/documenttypes/edit/{0}", currentDocumentTypeId); - - //Replace the doc type property - var docTypeProperty = genericProperties.First(x => x.Alias == string.Format("{0}doctype", Constants.PropertyEditors.InternalGenericPropertiesPrefix)); - docTypeProperty.Value = new List - { - new - { - linkText = currentDocumentTypeName, - url = docTypeLink, - target = "_self", - icon = "icon-item-arrangement" - } - }; - //TODO: Hard coding this because the templatepicker doesn't necessarily need to be a resolvable (real) property editor - docTypeProperty.View = "urllist"; - } - - // inject 'Link to document' as the first generic property - genericProperties.Insert(0, new ContentPropertyDisplay - { - Alias = string.Format("{0}urls", Constants.PropertyEditors.InternalGenericPropertiesPrefix), - Label = localizedText.Localize("content/urls"), - Value = string.Join(",", display.Urls), - View = "urllist" //TODO: Hard coding this because the templatepicker doesn't necessarily need to be a resolvable (real) property editor - }); - }); - } - - /// - //TODO: This is horribly inneficient - /// Creates the list of action buttons allowed for this user - Publish, Send to publish, save, unpublish returned as the button's 'letter' - /// - private class ActionButtonsResolver : ValueResolver> - { - private readonly Lazy _userService; - - public ActionButtonsResolver(Lazy userService) - { - _userService = userService; - } - - protected override IEnumerable ResolveCore(IContent source) - { - if (UmbracoContext.Current == null) - { - //cannot check permissions without a context - return Enumerable.Empty(); - } - var svc = _userService.Value; - - var permissions = svc.GetPermissions( - //TODO: This is certainly not ideal usage here - perhaps the best way to deal with this in the future is - // with the IUmbracoContextAccessor. In the meantime, if used outside of a web app this will throw a null - // refrence exception :( - UmbracoContext.Current.Security.CurrentUser, - // Here we need to do a special check since this could be new content, in which case we need to get the permissions - // from the parent, not the existing one otherwise permissions would be coming from the root since Id is 0. - source.HasIdentity ? source.Id : source.ParentId) - .FirstOrDefault(); - - return permissions == null - ? Enumerable.Empty() - : permissions.AssignedPermissions.Where(x => x.Length == 1).Select(x => x.ToUpperInvariant()[0]); - } - } - } +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Web; +using System.Web.Mvc; +using AutoMapper; +using Umbraco.Core; +using Umbraco.Core.Models; +using Umbraco.Core.Services; +using Umbraco.Web.Composing; +using Umbraco.Web.Models.ContentEditing; +using Umbraco.Web.Trees; +using Umbraco.Web.Routing; +using Umbraco.Web._Legacy.Actions; + +namespace Umbraco.Web.Models.Mapping +{ + /// + /// Declares how model mappings for content + /// + internal class ContentProfile : Profile + { + public ContentProfile(IUserService userService, ILocalizedTextService textService, IContentService contentService, IContentTypeService contentTypeService, IDataTypeService dataTypeService) + { + // create, capture, cache + var contentOwnerResolver = new OwnerResolver(userService); + var creatorResolver = new CreatorResolver(userService); + var actionButtonsResolver = new ActionButtonsResolver(new Lazy(() => userService)); + var tabsAndPropertiesResolver = new TabsAndPropertiesResolver(textService); + + //FROM IContent TO ContentItemDisplay + CreateMap() + .ForMember(dest => dest.Udi, opt => opt.MapFrom(src => Udi.Create(Constants.UdiEntityType.Document, src.Key))) + .ForMember(dest => dest.Owner, opt => opt.ResolveUsing(src => contentOwnerResolver.Resolve(src))) + .ForMember(dest => dest.Updater, opt => opt.ResolveUsing(src => creatorResolver.Resolve(src))) + .ForMember(dest => dest.Icon, opt => opt.MapFrom(src => src.ContentType.Icon)) + .ForMember(dest => dest.ContentTypeAlias, opt => opt.MapFrom(src => src.ContentType.Alias)) + .ForMember(dest => dest.ContentTypeName, opt => opt.MapFrom(src => src.ContentType.Name)) + .ForMember(dest => dest.IsContainer, opt => opt.MapFrom(src => src.ContentType.IsContainer)) + .ForMember(dest => dest.IsChildOfListView, opt => opt.Ignore()) + .ForMember(dest => dest.Trashed, opt => opt.MapFrom(src => src.Trashed)) + .ForMember(dest => dest.PublishDate, opt => opt.MapFrom(src => GetPublishedDate(src))) + .ForMember(dest => dest.TemplateAlias, opt => opt.MapFrom(src => src.Template.Alias)) + .ForMember(dest => dest.HasPublishedVersion, opt => opt.MapFrom(src => src.HasPublishedVersion)) + .ForMember(dest => dest.Urls, opt => opt.MapFrom(src => + UmbracoContext.Current == null + ? new[] {"Cannot generate urls without a current Umbraco Context"} + : src.GetContentUrls(UmbracoContext.Current))) + .ForMember(dest => dest.Properties, opt => opt.Ignore()) + .ForMember(dest => dest.AllowPreview, opt => opt.Ignore()) + .ForMember(dest => dest.TreeNodeUrl, opt => opt.Ignore()) + .ForMember(dest => dest.Notifications, opt => opt.Ignore()) + .ForMember(dest => dest.Errors, opt => opt.Ignore()) + .ForMember(dest => dest.Alias, opt => opt.Ignore()) + .ForMember(dest => dest.Tabs, opt => opt.ResolveUsing(src => tabsAndPropertiesResolver.Resolve(src))) + .ForMember(dest => dest.AllowedActions, opt => opt.ResolveUsing(src => actionButtonsResolver.Resolve(src))) + .AfterMap((src, dest) => AfterMap(src, dest, dataTypeService, textService, contentTypeService, contentService)); + + //FROM IContent TO ContentItemBasic + CreateMap>() + .ForMember(dest => dest.Udi, opt => opt.MapFrom(src => Udi.Create(Constants.UdiEntityType.Document, src.Key))) + .ForMember(dest => dest.Owner, opt => opt.ResolveUsing(src => contentOwnerResolver.Resolve(src))) + .ForMember(dest => dest.Updater, opt => opt.ResolveUsing(src => creatorResolver.Resolve(src))) + .ForMember(dest => dest.Icon, opt => opt.MapFrom(src => src.ContentType.Icon)) + .ForMember(dest => dest.Trashed, opt => opt.MapFrom(src => src.Trashed)) + .ForMember(dest => dest.HasPublishedVersion, opt => opt.MapFrom(src => src.HasPublishedVersion)) + .ForMember(dest => dest.ContentTypeAlias, opt => opt.MapFrom(src => src.ContentType.Alias)) + .ForMember(dest => dest.Alias, opt => opt.Ignore()); + + //FROM IContent TO ContentItemDto + CreateMap>() + .ForMember(dest => dest.Udi, opt => opt.MapFrom(src => Udi.Create(Constants.UdiEntityType.Document, src.Key))) + .ForMember(dest => dest.Owner, opt => opt.ResolveUsing(src => contentOwnerResolver.Resolve(src))) + .ForMember(dest => dest.HasPublishedVersion, opt => opt.MapFrom(src => src.HasPublishedVersion)) + .ForMember(dest => dest.Updater, opt => opt.Ignore()) + .ForMember(dest => dest.Icon, opt => opt.Ignore()) + .ForMember(dest => dest.Alias, opt => opt.Ignore()); + } + + private static DateTime? GetPublishedDate(IContent content) + { + var date = ((Content) content).PublishedDate; + return date == default (DateTime) ? (DateTime?) null : date; + } + + /// + /// Maps the generic tab with custom properties for content + /// + /// + /// + /// + /// + /// + /// + private static void AfterMap(IContent content, ContentItemDisplay display, IDataTypeService dataTypeService, + ILocalizedTextService localizedText, IContentTypeService contentTypeService, IContentService contentService) + { + // map the IsChildOfListView (this is actually if it is a descendant of a list view!) + var parent = content.Parent(contentService); + display.IsChildOfListView = parent != null && (parent.ContentType.IsContainer || contentTypeService.HasContainerInPath(parent.Path)); + + //map the tree node url + if (HttpContext.Current != null) + { + var urlHelper = new UrlHelper(HttpContext.Current.Request.RequestContext); + var url = urlHelper.GetUmbracoApiService(controller => controller.GetTreeNode(display.Id.ToString(), null)); + display.TreeNodeUrl = url; + } + + //fill in the template config to be passed to the template drop down. + var templateItemConfig = new Dictionary {{"", "Choose..."}}; + foreach (var t in content.ContentType.AllowedTemplates + .Where(t => t.Alias.IsNullOrWhiteSpace() == false && t.Name.IsNullOrWhiteSpace() == false)) + { + templateItemConfig.Add(t.Alias, t.Name); + } + + if (content.ContentType.IsContainer) + { + TabsAndPropertiesResolver.AddListView(display, "content", dataTypeService, localizedText); + } + + var properties = new List + { + new ContentPropertyDisplay + { + Alias = string.Format("{0}doctype", Constants.PropertyEditors.InternalGenericPropertiesPrefix), + Label = localizedText.Localize("content/documentType"), + Value = localizedText.UmbracoDictionaryTranslate(display.ContentTypeName), + View = Current.PropertyEditors[Constants.PropertyEditors.NoEditAlias].ValueEditor.View + }, + new ContentPropertyDisplay + { + Alias = string.Format("{0}releasedate", Constants.PropertyEditors.InternalGenericPropertiesPrefix), + Label = localizedText.Localize("content/releaseDate"), + Value = display.ReleaseDate.HasValue ? display.ReleaseDate.Value.ToIsoString() : null, + //Not editible for people without publish permission (U4-287) + View = display.AllowedActions.Contains(ActionPublish.Instance.Letter) ? "datepicker" : Current.PropertyEditors[Constants.PropertyEditors.NoEditAlias].ValueEditor.View, + Config = new Dictionary + { + {"offsetTime", "1"} + } + //TODO: Fix up hard coded datepicker + }, + new ContentPropertyDisplay + { + Alias = string.Format("{0}expiredate", Constants.PropertyEditors.InternalGenericPropertiesPrefix), + Label = localizedText.Localize("content/unpublishDate"), + Value = display.ExpireDate.HasValue ? display.ExpireDate.Value.ToIsoString() : null, + //Not editible for people without publish permission (U4-287) + View = display.AllowedActions.Contains(ActionPublish.Instance.Letter) ? "datepicker" : Current.PropertyEditors[Constants.PropertyEditors.NoEditAlias].ValueEditor.View, + Config = new Dictionary + { + {"offsetTime", "1"} + } + //TODO: Fix up hard coded datepicker + }, + new ContentPropertyDisplay + { + Alias = string.Format("{0}template", Constants.PropertyEditors.InternalGenericPropertiesPrefix), + Label = localizedText.Localize("template/template"), + Value = display.TemplateAlias, + View = "dropdown", //TODO: Hard coding until we make a real dropdown property editor to lookup + Config = new Dictionary + { + {"items", templateItemConfig} + } + } + }; + + + TabsAndPropertiesResolver.MapGenericProperties(content, display, localizedText, properties.ToArray(), + genericProperties => + { + //TODO: This would be much nicer with the IUmbracoContextAccessor so we don't use singletons + //If this is a web request and there's a user signed in and the + // user has access to the settings section, we will + if (HttpContext.Current != null && UmbracoContext.Current != null && UmbracoContext.Current.Security.CurrentUser != null + && UmbracoContext.Current.Security.CurrentUser.AllowedSections.Any(x => x.Equals(Constants.Applications.Settings))) + { + var currentDocumentType = contentTypeService.Get(display.ContentTypeAlias); + var currentDocumentTypeName = currentDocumentType == null ? string.Empty : localizedText.UmbracoDictionaryTranslate(currentDocumentType.Name); + + var currentDocumentTypeId = currentDocumentType == null ? string.Empty : currentDocumentType.Id.ToString(CultureInfo.InvariantCulture); + //TODO: Hard coding this is not good + var docTypeLink = string.Format("#/settings/documenttypes/edit/{0}", currentDocumentTypeId); + + //Replace the doc type property + var docTypeProperty = genericProperties.First(x => x.Alias == string.Format("{0}doctype", Constants.PropertyEditors.InternalGenericPropertiesPrefix)); + docTypeProperty.Value = new List + { + new + { + linkText = currentDocumentTypeName, + url = docTypeLink, + target = "_self", + icon = "icon-item-arrangement" + } + }; + //TODO: Hard coding this because the templatepicker doesn't necessarily need to be a resolvable (real) property editor + docTypeProperty.View = "urllist"; + } + + // inject 'Link to document' as the first generic property + genericProperties.Insert(0, new ContentPropertyDisplay + { + Alias = string.Format("{0}urls", Constants.PropertyEditors.InternalGenericPropertiesPrefix), + Label = localizedText.Localize("content/urls"), + Value = string.Join(",", display.Urls), + View = "urllist" //TODO: Hard coding this because the templatepicker doesn't necessarily need to be a resolvable (real) property editor + }); + }); + } + } } \ No newline at end of file diff --git a/src/Umbraco.Web/Models/Mapping/ContentPropertyBasicConverter.cs b/src/Umbraco.Web/Models/Mapping/ContentPropertyBasicConverter.cs index 89ff4756d0..bcd420e16c 100644 --- a/src/Umbraco.Web/Models/Mapping/ContentPropertyBasicConverter.cs +++ b/src/Umbraco.Web/Models/Mapping/ContentPropertyBasicConverter.cs @@ -13,11 +13,10 @@ namespace Umbraco.Web.Models.Mapping /// /// Creates a base generic ContentPropertyBasic from a Property /// - /// - internal class ContentPropertyBasicConverter : TypeConverter - where T : ContentPropertyBasic, new() + internal class ContentPropertyBasicConverter : ITypeConverter + where TDestination : ContentPropertyBasic, new() { - protected Lazy DataTypeService { get; private set; } + protected Lazy DataTypeService { get; } public ContentPropertyBasicConverter(Lazy dataTypeService) { @@ -27,24 +26,23 @@ namespace Umbraco.Web.Models.Mapping /// /// Assigns the PropertyEditor, Id, Alias and Value to the property /// - /// /// - protected override T ConvertCore(Property property) + public virtual TDestination Convert(Property property, TDestination dest, ResolutionContext context) { var editor = Current.PropertyEditors[property.PropertyType.PropertyEditorAlias]; if (editor == null) { - Current.Logger.Error>( + Current.Logger.Error>( "No property editor found, converting to a Label", new NullReferenceException("The property editor with alias " + property.PropertyType.PropertyEditorAlias + " does not exist")); editor = Current.PropertyEditors[Constants.PropertyEditors.NoEditAlias]; } - var result = new T + var result = new TDestination { Id = property.Id, Value = editor.ValueEditor.ConvertDbToEditor(property, property.PropertyType, DataTypeService.Value), - Alias = property.Alias, + Alias = property.Alias, PropertyEditor = editor, Editor = editor.Alias }; diff --git a/src/Umbraco.Web/Models/Mapping/ContentPropertyDisplayConverter.cs b/src/Umbraco.Web/Models/Mapping/ContentPropertyDisplayConverter.cs index 20ff209371..72be760ce6 100644 --- a/src/Umbraco.Web/Models/Mapping/ContentPropertyDisplayConverter.cs +++ b/src/Umbraco.Web/Models/Mapping/ContentPropertyDisplayConverter.cs @@ -1,5 +1,6 @@ using System; using System.Linq; +using AutoMapper; using Umbraco.Core; using Umbraco.Core.Configuration; using Umbraco.Core.Models; @@ -15,13 +16,11 @@ namespace Umbraco.Web.Models.Mapping { public ContentPropertyDisplayConverter(Lazy dataTypeService) : base(dataTypeService) - { + { } - } - - protected override ContentPropertyDisplay ConvertCore(Property originalProp) + public override ContentPropertyDisplay Convert(Property originalProp, ContentPropertyDisplay dest, ResolutionContext context) { - var display = base.ConvertCore(originalProp); + var display = base.Convert(originalProp, dest, context); var dataTypeService = DataTypeService.Value; var preVals = dataTypeService.GetPreValuesCollectionByDataTypeId(originalProp.PropertyType.DataTypeDefinitionId); diff --git a/src/Umbraco.Web/Models/Mapping/ContentPropertyDtoConverter.cs b/src/Umbraco.Web/Models/Mapping/ContentPropertyDtoConverter.cs index 3a6e199f96..23fa60c551 100644 --- a/src/Umbraco.Web/Models/Mapping/ContentPropertyDtoConverter.cs +++ b/src/Umbraco.Web/Models/Mapping/ContentPropertyDtoConverter.cs @@ -1,7 +1,6 @@ using System; -using Umbraco.Core; +using AutoMapper; using Umbraco.Core.Models; -using Umbraco.Core.PropertyEditors; using Umbraco.Core.Services; using Umbraco.Web.Models.ContentEditing; @@ -14,12 +13,11 @@ namespace Umbraco.Web.Models.Mapping { public ContentPropertyDtoConverter(Lazy dataTypeService) : base(dataTypeService) - { - } + { } - protected override ContentPropertyDto ConvertCore(Property originalProperty) + public override ContentPropertyDto Convert(Property originalProperty, ContentPropertyDto dest, ResolutionContext context) { - var propertyDto = base.ConvertCore(originalProperty); + var propertyDto = base.Convert(originalProperty, dest, context); var dataTypeService = DataTypeService.Value; diff --git a/src/Umbraco.Web/Models/Mapping/ContentPropertyModelMapper.cs b/src/Umbraco.Web/Models/Mapping/ContentPropertyProfile.cs similarity index 71% rename from src/Umbraco.Web/Models/Mapping/ContentPropertyModelMapper.cs rename to src/Umbraco.Web/Models/Mapping/ContentPropertyProfile.cs index 3f92acd1af..926025d838 100644 --- a/src/Umbraco.Web/Models/Mapping/ContentPropertyModelMapper.cs +++ b/src/Umbraco.Web/Models/Mapping/ContentPropertyProfile.cs @@ -1,47 +1,43 @@ -using System; -using AutoMapper; -using Umbraco.Core.Models; -using Umbraco.Core.Models.Mapping; -using Umbraco.Core.Services; -using Umbraco.Web.Models.ContentEditing; - -namespace Umbraco.Web.Models.Mapping -{ - /// - /// A mapper which declares how to map content properties. These mappings are shared among media (and probably members) which is - /// why they are in their own mapper - /// - internal class ContentPropertyModelMapper : ModelMapperConfiguration - { - private readonly IDataTypeService _dataTypeService; - - public ContentPropertyModelMapper(IDataTypeService dataTypeService) - { - _dataTypeService = dataTypeService; - } - - public override void ConfigureMappings(IMapperConfiguration config) - { - var lazyDataTypeService = new Lazy(() => _dataTypeService); - - //FROM Property TO ContentPropertyBasic - config.CreateMap>() - .ForMember(tab => tab.Label, expression => expression.MapFrom(@group => @group.Name)) - .ForMember(tab => tab.IsActive, expression => expression.UseValue(true)) - .ForMember(tab => tab.Properties, expression => expression.Ignore()) - .ForMember(tab => tab.Alias, expression => expression.Ignore()); - - //FROM Property TO ContentPropertyBasic - config.CreateMap() - .ConvertUsing(new ContentPropertyBasicConverter(lazyDataTypeService)); - - //FROM Property TO ContentPropertyDto - config.CreateMap() - .ConvertUsing(new ContentPropertyDtoConverter(lazyDataTypeService)); - - //FROM Property TO ContentPropertyDisplay - config.CreateMap() - .ConvertUsing(new ContentPropertyDisplayConverter(lazyDataTypeService)); - } - } +using System; +using AutoMapper; +using Umbraco.Core.Models; +using Umbraco.Core.Services; +using Umbraco.Web.Models.ContentEditing; + +namespace Umbraco.Web.Models.Mapping +{ + /// + /// A mapper which declares how to map content properties. These mappings are shared among media (and probably members) which is + /// why they are in their own mapper + /// + internal class ContentPropertyProfile : Profile + { + private readonly IDataTypeService _dataTypeService; + + public ContentPropertyProfile(IDataTypeService dataTypeService) + { + _dataTypeService = dataTypeService; + + var lazyDataTypeService = new Lazy(() => _dataTypeService); + + //FROM Property TO ContentPropertyBasic + CreateMap>() + .ForMember(tab => tab.Label, expression => expression.MapFrom(@group => @group.Name)) + .ForMember(tab => tab.IsActive, expression => expression.UseValue(true)) + .ForMember(tab => tab.Properties, expression => expression.Ignore()) + .ForMember(tab => tab.Alias, expression => expression.Ignore()); + + //FROM Property TO ContentPropertyBasic + CreateMap() + .ConvertUsing(new ContentPropertyBasicConverter(lazyDataTypeService)); + + //FROM Property TO ContentPropertyDto + CreateMap() + .ConvertUsing(new ContentPropertyDtoConverter(lazyDataTypeService)); + + //FROM Property TO ContentPropertyDisplay + CreateMap() + .ConvertUsing(new ContentPropertyDisplayConverter(lazyDataTypeService)); + } + } } \ No newline at end of file diff --git a/src/Umbraco.Web/Models/Mapping/ContentTypeModelMapperExtensions.cs b/src/Umbraco.Web/Models/Mapping/ContentTypeModelMapperExtensions.cs index 5f7e1c8b61..c33e1f00f1 100644 --- a/src/Umbraco.Web/Models/Mapping/ContentTypeModelMapperExtensions.cs +++ b/src/Umbraco.Web/Models/Mapping/ContentTypeModelMapperExtensions.cs @@ -20,14 +20,13 @@ namespace Umbraco.Web.Models.Mapping /// internal static class ContentTypeModelMapperExtensions { - public static IMappingExpression MapPropertyGroupBasicToPropertyGroupPersistence( this IMappingExpression mapping) where TSource : PropertyGroupBasic where TPropertyTypeBasic : PropertyTypeBasic { return mapping - .ForMember(dest => dest.Id, map => map.Condition(source => source.Id > 0)) + .ForMember(dest => dest.Id, map => map.Condition(src => src.Id > 0)) .ForMember(dest => dest.Key, map => map.Ignore()) .ForMember(dest => dest.HasIdentity, map => map.Ignore()) .ForMember(dest => dest.CreateDate, map => map.Ignore()) @@ -43,11 +42,11 @@ namespace Umbraco.Web.Models.Mapping where TPropertyTypeDisplay : PropertyTypeDisplay { return mapping - .ForMember(dest => dest.Id, expression => expression.Condition(source => source.Id > 0)) - .ForMember(g => g.ContentTypeId, expression => expression.Ignore()) - .ForMember(g => g.ParentTabContentTypes, expression => expression.Ignore()) - .ForMember(g => g.ParentTabContentTypeNames, expression => expression.Ignore()) - .ForMember(g => g.Properties, expression => expression.MapFrom(display => display.Properties.Select(Mapper.Map))); + .ForMember(dest => dest.Id, opt => opt.Condition(src => src.Id > 0)) + .ForMember(dest => dest.ContentTypeId, opt => opt.Ignore()) + .ForMember(dest => dest.ParentTabContentTypes, opt => opt.Ignore()) + .ForMember(dest => dest.ParentTabContentTypeNames, opt => opt.Ignore()) + .ForMember(dest => dest.Properties, opt => opt.MapFrom(src => src.Properties.Select(Mapper.Map))); } public static void AfterMapContentTypeSaveToEntity(TSource source, TDestination dest, IContentTypeService contentTypeService) @@ -107,14 +106,16 @@ namespace Umbraco.Web.Models.Mapping where TPropertyTypeDestination : PropertyTypeDisplay where TPropertyTypeSource : PropertyTypeBasic { + var propertyGroupDisplayResolver = new PropertyGroupDisplayResolver(); + return mapping - .ForMember(dto => dto.CreateDate, expression => expression.Ignore()) - .ForMember(dto => dto.UpdateDate, expression => expression.Ignore()) - .ForMember(dto => dto.ListViewEditorName, expression => expression.Ignore()) - .ForMember(dto => dto.Notifications, expression => expression.Ignore()) - .ForMember(dto => dto.Errors, expression => expression.Ignore()) - .ForMember(dto => dto.LockedCompositeContentTypes, exp => exp.Ignore()) - .ForMember(dto => dto.Groups, expression => expression.ResolveUsing(new PropertyGroupDisplayResolver())); + .ForMember(dest => dest.CreateDate, opt => opt.Ignore()) + .ForMember(dest => dest.UpdateDate, opt => opt.Ignore()) + .ForMember(dest => dest.ListViewEditorName, opt => opt.Ignore()) + .ForMember(dest => dest.Notifications, opt => opt.Ignore()) + .ForMember(dest => dest.Errors, opt => opt.Ignore()) + .ForMember(dest => dest.LockedCompositeContentTypes, opt => opt.Ignore()) + .ForMember(dest => dest.Groups, opt => opt.ResolveUsing(src => propertyGroupDisplayResolver.Resolve(src))); } public static IMappingExpression MapBaseContentTypeEntityToDisplay( @@ -124,30 +125,23 @@ namespace Umbraco.Web.Models.Mapping where TDestination : ContentTypeCompositionDisplay where TPropertyTypeDisplay : PropertyTypeDisplay, new() { + var contentTypeUdiResolver = new ContentTypeUdiResolver(); + var lockedCompositionsResolver = new LockedCompositionsResolver(contentTypeService); + var propertyTypeGroupResolver = new PropertyTypeGroupResolver(propertyEditors, dataTypeService); + return mapping - .ForMember(x => x.Udi, expression => expression.ResolveUsing(new ContentTypeUdiResolver())) - .ForMember(display => display.Notifications, expression => expression.Ignore()) - .ForMember(display => display.Errors, expression => expression.Ignore()) - .ForMember(display => display.AllowAsRoot, expression => expression.MapFrom(type => type.AllowedAsRoot)) - .ForMember(display => display.ListViewEditorName, expression => expression.Ignore()) + .ForMember(dest => dest.Udi, opt => opt.ResolveUsing(src => contentTypeUdiResolver.Resolve(src))) + .ForMember(dest => dest.Notifications, opt => opt.Ignore()) + .ForMember(dest => dest.Errors, opt => opt.Ignore()) + .ForMember(dest => dest.AllowAsRoot, opt => opt.MapFrom(src => src.AllowedAsRoot)) + .ForMember(dest => dest.ListViewEditorName, opt => opt.Ignore()) //Ignore because this is not actually used for content types - .ForMember(display => display.Trashed, expression => expression.Ignore()) + .ForMember(dest => dest.Trashed, opt => opt.Ignore()) - .ForMember( - dto => dto.AllowedContentTypes, - expression => expression.MapFrom(dto => dto.AllowedContentTypes.Select(x => x.Id.Value))) - - .ForMember( - dto => dto.CompositeContentTypes, - expression => expression.MapFrom(dto => dto.ContentTypeComposition)) - - .ForMember( - dto => dto.LockedCompositeContentTypes, - expression => expression.ResolveUsing(new LockedCompositionsResolver(contentTypeService))) - - .ForMember( - dto => dto.Groups, - expression => expression.ResolveUsing(new PropertyTypeGroupResolver(propertyEditors, dataTypeService))); + .ForMember(dest => dest.AllowedContentTypes, opt => opt.MapFrom(src => src.AllowedContentTypes.Select(x => x.Id.Value))) + .ForMember(dest => dest.CompositeContentTypes, opt => opt.MapFrom(src => src.ContentTypeComposition)) + .ForMember(dest => dest.LockedCompositeContentTypes, opt => opt.ResolveUsing(src => lockedCompositionsResolver.Resolve(src))) + .ForMember(dest => dest.Groups, opt => opt.ResolveUsing(src => propertyTypeGroupResolver.Resolve(src))); } /// @@ -167,29 +161,29 @@ namespace Umbraco.Web.Models.Mapping { return mapping //only map id if set to something higher then zero - .ForMember(dto => dto.Id, expression => expression.Condition(display => (Convert.ToInt32(display.Id) > 0))) - .ForMember(dto => dto.Id, expression => expression.MapFrom(display => Convert.ToInt32(display.Id))) + .ForMember(dest => dest.Id, opt => opt.Condition(src => (Convert.ToInt32(src.Id) > 0))) + .ForMember(dest => dest.Id, opt => opt.MapFrom(src => Convert.ToInt32(src.Id))) //These get persisted as part of the saving procedure, nothing to do with the display model - .ForMember(dto => dto.CreateDate, expression => expression.Ignore()) - .ForMember(dto => dto.UpdateDate, expression => expression.Ignore()) - .ForMember(dto => dto.DeletedDate, expression => expression.Ignore()) + .ForMember(dest => dest.CreateDate, opt => opt.Ignore()) + .ForMember(dest => dest.UpdateDate, opt => opt.Ignore()) + .ForMember(dest => dest.DeletedDate, opt => opt.Ignore()) - .ForMember(dto => dto.AllowedAsRoot, expression => expression.MapFrom(display => display.AllowAsRoot)) - .ForMember(dto => dto.CreatorId, expression => expression.Ignore()) - .ForMember(dto => dto.Level, expression => expression.Ignore()) - .ForMember(dto => dto.SortOrder, expression => expression.Ignore()) + .ForMember(dest => dest.AllowedAsRoot, opt => opt.MapFrom(src => src.AllowAsRoot)) + .ForMember(dest => dest.CreatorId, opt => opt.Ignore()) + .ForMember(dest => dest.Level, opt => opt.Ignore()) + .ForMember(dest => dest.SortOrder, opt => opt.Ignore()) //ignore, we'll do this in after map - .ForMember(dto => dto.PropertyGroups, expression => expression.Ignore()) - .ForMember(dto => dto.NoGroupPropertyTypes, expression => expression.Ignore()) + .ForMember(dest => dest.PropertyGroups, opt => opt.Ignore()) + .ForMember(dest => dest.NoGroupPropertyTypes, opt => opt.Ignore()) // ignore, composition is managed in AfterMapContentTypeSaveToEntity .ForMember(dest => dest.ContentTypeComposition, opt => opt.Ignore()) .ForMember( - dto => dto.AllowedContentTypes, - expression => expression.MapFrom(dto => dto.AllowedContentTypes.Select((t, i) => new ContentTypeSort(t, i)))) + dest => dest.AllowedContentTypes, + opt => opt.MapFrom(src => src.AllowedContentTypes.Select((t, i) => new ContentTypeSort(t, i)))) - .AfterMap((source, dest) => + .AfterMap((src, dest) => { // handle property groups and property types // note that ContentTypeSave has @@ -211,7 +205,7 @@ namespace Umbraco.Web.Models.Mapping var destOrigGroups = dest.PropertyGroups.ToArray(); // local groups var destOrigProperties = dest.PropertyTypes.ToArray(); // all properties, in groups or not var destGroups = new List(); - var sourceGroups = source.Groups.Where(x => x.IsGenericProperties == false).ToArray(); + var sourceGroups = src.Groups.Where(x => x.IsGenericProperties == false).ToArray(); foreach (var sourceGroup in sourceGroups) { // get the dest group @@ -242,7 +236,7 @@ namespace Umbraco.Web.Models.Mapping // the old groups - they are just gone and will be cleared by the repository // handle non-grouped (ie generic) properties - var genericPropertiesGroup = source.Groups.FirstOrDefault(x => x.IsGenericProperties); + var genericPropertiesGroup = src.Groups.FirstOrDefault(x => x.IsGenericProperties); if (genericPropertiesGroup != null) { // handle local properties diff --git a/src/Umbraco.Web/Models/Mapping/ContentTypeModelMapper.cs b/src/Umbraco.Web/Models/Mapping/ContentTypeProfile.cs similarity index 86% rename from src/Umbraco.Web/Models/Mapping/ContentTypeModelMapper.cs rename to src/Umbraco.Web/Models/Mapping/ContentTypeProfile.cs index 6c0832a80e..444dd7c546 100644 --- a/src/Umbraco.Web/Models/Mapping/ContentTypeModelMapper.cs +++ b/src/Umbraco.Web/Models/Mapping/ContentTypeProfile.cs @@ -1,280 +1,283 @@ -using System; -using System.Linq; -using AutoMapper; -using Umbraco.Core; -using Umbraco.Core.Models; -using Umbraco.Core.Models.Mapping; -using Umbraco.Core.PropertyEditors; -using Umbraco.Web.Models.ContentEditing; -using Umbraco.Core.Services; - -namespace Umbraco.Web.Models.Mapping -{ - /// - /// Defines mappings for content/media/members type mappings - /// - internal class ContentTypeModelMapper : ModelMapperConfiguration - { - private readonly PropertyEditorCollection _propertyEditors; - private readonly IDataTypeService _dataTypeService; - private readonly IFileService _fileService; - private readonly IContentTypeService _contentTypeService; - private readonly IMediaTypeService _mediaTypeService; - - public ContentTypeModelMapper(PropertyEditorCollection propertyEditors, IDataTypeService dataTypeService, IFileService fileService, IContentTypeService contentTypeService, IMediaTypeService mediaTypeService) - { - _propertyEditors = propertyEditors; - _dataTypeService = dataTypeService; - _fileService = fileService; - _contentTypeService = contentTypeService; - _mediaTypeService = mediaTypeService; - } - - public override void ConfigureMappings(IMapperConfiguration config) - { - - config.CreateMap() - .ConstructUsing(basic => new PropertyType(_dataTypeService.GetDataTypeDefinitionById(basic.DataTypeId))) - .ForMember(type => type.ValidationRegExp, expression => expression.ResolveUsing(basic => basic.Validation.Pattern)) - .ForMember(type => type.Mandatory, expression => expression.ResolveUsing(basic => basic.Validation.Mandatory)) - .ForMember(type => type.Name, expression => expression.ResolveUsing(basic => basic.Label)) - .ForMember(type => type.DataTypeDefinitionId, expression => expression.ResolveUsing(basic => basic.DataTypeId)) - .ForMember(type => type.DataTypeId, expression => expression.Ignore()) - .ForMember(type => type.PropertyEditorAlias, expression => expression.Ignore()) - .ForMember(type => type.HelpText, expression => expression.Ignore()) - .ForMember(type => type.Key, expression => expression.Ignore()) - .ForMember(type => type.CreateDate, expression => expression.Ignore()) - .ForMember(type => type.UpdateDate, expression => expression.Ignore()) - .ForMember(type => type.DeletedDate, expression => expression.Ignore()) - .ForMember(type => type.HasIdentity, expression => expression.Ignore()); - - config.CreateMap() - //do the base mapping - .MapBaseContentTypeSaveToEntity() - .ConstructUsing((source) => new ContentType(source.ParentId)) - .ForMember(source => source.AllowedTemplates, expression => expression.Ignore()) - .ForMember(dto => dto.DefaultTemplate, expression => expression.Ignore()) - .AfterMap((source, dest) => - { - dest.AllowedTemplates = source.AllowedTemplates - .Where(x => x != null) - .Select(s => _fileService.GetTemplate(s)) - .ToArray(); - - if (source.DefaultTemplate != null) - dest.SetDefaultTemplate(_fileService.GetTemplate(source.DefaultTemplate)); - - ContentTypeModelMapperExtensions.AfterMapContentTypeSaveToEntity(source, dest, _contentTypeService); - }); - - config.CreateMap() - //do the base mapping - .MapBaseContentTypeSaveToEntity() - .ConstructUsing((source) => new MediaType(source.ParentId)) - .AfterMap((source, dest) => - { - ContentTypeModelMapperExtensions.AfterMapMediaTypeSaveToEntity(source, dest, _mediaTypeService); - }); - - config.CreateMap() - //do the base mapping - .MapBaseContentTypeSaveToEntity() - .ConstructUsing((source) => new MemberType(source.ParentId)) - .AfterMap((source, dest) => - { - ContentTypeModelMapperExtensions.AfterMapContentTypeSaveToEntity(source, dest, _contentTypeService); - - //map the MemberCanEditProperty,MemberCanViewProperty - foreach (var propertyType in source.Groups.SelectMany(x => x.Properties)) - { - var localCopy = propertyType; - var destProp = dest.PropertyTypes.SingleOrDefault(x => x.Alias.InvariantEquals(localCopy.Alias)); - if (destProp != null) - { - dest.SetMemberCanEditProperty(localCopy.Alias, localCopy.MemberCanEditProperty); - dest.SetMemberCanViewProperty(localCopy.Alias, localCopy.MemberCanViewProperty); - } - } - }); - - config.CreateMap().ConvertUsing(x => x.Alias); - - config.CreateMap() - //map base logic - .MapBaseContentTypeEntityToDisplay(_propertyEditors, _dataTypeService, _contentTypeService) - .AfterMap((memberType, display) => - { - //map the MemberCanEditProperty,MemberCanViewProperty - foreach (var propertyType in memberType.PropertyTypes) - { - var localCopy = propertyType; - var displayProp = display.Groups.SelectMany(x => x.Properties).SingleOrDefault(x => x.Alias.InvariantEquals(localCopy.Alias)); - if (displayProp != null) - { - displayProp.MemberCanEditProperty = memberType.MemberCanEditProperty(localCopy.Alias); - displayProp.MemberCanViewProperty = memberType.MemberCanViewProperty(localCopy.Alias); - } - } - }); - - config.CreateMap() - //map base logic - .MapBaseContentTypeEntityToDisplay(_propertyEditors, _dataTypeService, _contentTypeService) - .AfterMap((source, dest) => - { - //default listview - dest.ListViewEditorName = Constants.Conventions.DataTypes.ListViewPrefix + "Media"; - - if (string.IsNullOrEmpty(source.Name) == false) - { - var name = Constants.Conventions.DataTypes.ListViewPrefix + source.Name; - if (_dataTypeService.GetDataTypeDefinitionByName(name) != null) - dest.ListViewEditorName = name; - } - }); - - config.CreateMap() - //map base logic - .MapBaseContentTypeEntityToDisplay(_propertyEditors, _dataTypeService, _contentTypeService) - .ForMember(dto => dto.AllowedTemplates, expression => expression.Ignore()) - .ForMember(dto => dto.DefaultTemplate, expression => expression.Ignore()) - .ForMember(display => display.Notifications, expression => expression.Ignore()) - .AfterMap((source, dest) => - { - //sync templates - dest.AllowedTemplates = source.AllowedTemplates.Select(Mapper.Map).ToArray(); - - if (source.DefaultTemplate != null) - dest.DefaultTemplate = Mapper.Map(source.DefaultTemplate); - - //default listview - dest.ListViewEditorName = Constants.Conventions.DataTypes.ListViewPrefix + "Content"; - - if (string.IsNullOrEmpty(source.Alias) == false) - { - var name = Constants.Conventions.DataTypes.ListViewPrefix + source.Alias; - if (_dataTypeService.GetDataTypeDefinitionByName(name) != null) - dest.ListViewEditorName = name; - } - - }); - - config.CreateMap() - .ForMember(x => x.Udi, expression => expression.MapFrom(content => Udi.Create(Constants.UdiEntityType.MemberType, content.Key))); - config.CreateMap() - .ForMember(x => x.Udi, expression => expression.MapFrom(content => Udi.Create(Constants.UdiEntityType.MediaType, content.Key))); - config.CreateMap() - .ForMember(x => x.Udi, expression => expression.MapFrom(content => Udi.Create(Constants.UdiEntityType.DocumentType, content.Key))); - - config.CreateMap() - - .ConstructUsing(propertyTypeBasic => - { - var dataType = _dataTypeService.GetDataTypeDefinitionById(propertyTypeBasic.DataTypeId); - if (dataType == null) throw new NullReferenceException("No data type found with id " + propertyTypeBasic.DataTypeId); - return new PropertyType(dataType, propertyTypeBasic.Alias); - }) - - //only map if it is actually set - .ForMember(dest => dest.Id, expression => expression.Condition(source => source.Id > 0)) - .ForMember(dto => dto.CreateDate, expression => expression.Ignore()) - .ForMember(dto => dto.UpdateDate, expression => expression.Ignore()) - //only map if it is actually set, if it's not set, it needs to be handled differently and will be taken care of in the - // IContentType.AddPropertyType - .ForMember(dest => dest.PropertyGroupId, expression => expression.Condition(source => source.GroupId > 0)) - .ForMember(type => type.PropertyGroupId, expression => expression.MapFrom(display => new Lazy(() => display.GroupId, false))) - .ForMember(type => type.Key, expression => expression.Ignore()) - .ForMember(type => type.HelpText, expression => expression.Ignore()) - .ForMember(type => type.HasIdentity, expression => expression.Ignore()) - //ignore because this is set in the ctor NOT ON UPDATE, STUPID! - //.ForMember(type => type.Alias, expression => expression.Ignore()) - //ignore because this is obsolete and shouldn't be used - .ForMember(type => type.DataTypeId, expression => expression.Ignore()) - .ForMember(type => type.Mandatory, expression => expression.MapFrom(display => display.Validation.Mandatory)) - .ForMember(type => type.ValidationRegExp, expression => expression.MapFrom(display => display.Validation.Pattern)) - .ForMember(type => type.DataTypeDefinitionId, expression => expression.MapFrom(display => display.DataTypeId)) - .ForMember(type => type.Name, expression => expression.MapFrom(display => display.Label)); - - #region *** Used for mapping on top of an existing display object from a save object *** - - config.CreateMap() - .MapBaseContentTypeSaveToDisplay(); - - config.CreateMap() - .MapBaseContentTypeSaveToDisplay(); - - config.CreateMap() - .MapBaseContentTypeSaveToDisplay() - .ForMember(dto => dto.AllowedTemplates, expression => expression.Ignore()) - .ForMember(dto => dto.DefaultTemplate, expression => expression.Ignore()) - .AfterMap((source, dest) => - { - //sync templates - var destAllowedTemplateAliases = dest.AllowedTemplates.Select(x => x.Alias); - //if the dest is set and it's the same as the source, then don't change - if (destAllowedTemplateAliases.SequenceEqual(source.AllowedTemplates) == false) - { - var templates = _fileService.GetTemplates(source.AllowedTemplates.ToArray()); - dest.AllowedTemplates = source.AllowedTemplates - .Select(x => Mapper.Map(templates.SingleOrDefault(t => t.Alias == x))) - .WhereNotNull() - .ToArray(); - } - - if (source.DefaultTemplate.IsNullOrWhiteSpace() == false) - { - //if the dest is set and it's the same as the source, then don't change - if (dest.DefaultTemplate == null || source.DefaultTemplate != dest.DefaultTemplate.Alias) - { - var template = _fileService.GetTemplate(source.DefaultTemplate); - dest.DefaultTemplate = template == null ? null : Mapper.Map(template); - } - } - else - { - dest.DefaultTemplate = null; - } - }); - - //for doc types, media types - config.CreateMap, PropertyGroup>() - .MapPropertyGroupBasicToPropertyGroupPersistence, PropertyTypeBasic>(); - - //for members - config.CreateMap, PropertyGroup>() - .MapPropertyGroupBasicToPropertyGroupPersistence, MemberPropertyTypeBasic>(); - - //for doc types, media types - config.CreateMap, PropertyGroupDisplay>() - .MapPropertyGroupBasicToPropertyGroupDisplay, PropertyTypeBasic, PropertyTypeDisplay>(); - - //for members - config.CreateMap, PropertyGroupDisplay>() - .MapPropertyGroupBasicToPropertyGroupDisplay, MemberPropertyTypeBasic, MemberPropertyTypeDisplay>(); - - config.CreateMap() - .ForMember(g => g.Editor, expression => expression.Ignore()) - .ForMember(g => g.View, expression => expression.Ignore()) - .ForMember(g => g.Config, expression => expression.Ignore()) - .ForMember(g => g.ContentTypeId, expression => expression.Ignore()) - .ForMember(g => g.ContentTypeName, expression => expression.Ignore()) - .ForMember(g => g.Locked, exp => exp.Ignore()); - - config.CreateMap() - .ForMember(g => g.Editor, expression => expression.Ignore()) - .ForMember(g => g.View, expression => expression.Ignore()) - .ForMember(g => g.Config, expression => expression.Ignore()) - .ForMember(g => g.ContentTypeId, expression => expression.Ignore()) - .ForMember(g => g.ContentTypeName, expression => expression.Ignore()) - .ForMember(g => g.Locked, exp => exp.Ignore()); - - #endregion - - - - - } - - - } +using System; +using System.Linq; +using AutoMapper; +using Umbraco.Core; +using Umbraco.Core.Models; +using Umbraco.Core.PropertyEditors; +using Umbraco.Web.Models.ContentEditing; +using Umbraco.Core.Services; + +namespace Umbraco.Web.Models.Mapping +{ + /// + /// Defines mappings for content/media/members type mappings + /// + internal class ContentTypeProfile : Profile + { + private readonly PropertyEditorCollection _propertyEditors; + private readonly IDataTypeService _dataTypeService; + private readonly IFileService _fileService; + private readonly IContentTypeService _contentTypeService; + private readonly IMediaTypeService _mediaTypeService; + + public ContentTypeProfile(PropertyEditorCollection propertyEditors, IDataTypeService dataTypeService, IFileService fileService, IContentTypeService contentTypeService, IMediaTypeService mediaTypeService) + { + _propertyEditors = propertyEditors; + _dataTypeService = dataTypeService; + _fileService = fileService; + _contentTypeService = contentTypeService; + _mediaTypeService = mediaTypeService; + + // v7 creates this map twice which makes no sense, and AutoMapper 6 detects it + // assuming the second map took over, and removing the first one for now + /* + CreateMap() + .ConstructUsing(basic => new PropertyType(_dataTypeService.GetDataTypeDefinitionById(basic.DataTypeId))) + .ForMember(type => type.ValidationRegExp, expression => expression.ResolveUsing(basic => basic.Validation.Pattern)) + .ForMember(type => type.Mandatory, expression => expression.ResolveUsing(basic => basic.Validation.Mandatory)) + .ForMember(type => type.Name, expression => expression.ResolveUsing(basic => basic.Label)) + .ForMember(type => type.DataTypeDefinitionId, expression => expression.ResolveUsing(basic => basic.DataTypeId)) + .ForMember(type => type.DataTypeId, expression => expression.Ignore()) + .ForMember(type => type.PropertyEditorAlias, expression => expression.Ignore()) + .ForMember(type => type.HelpText, expression => expression.Ignore()) + .ForMember(type => type.Key, expression => expression.Ignore()) + .ForMember(type => type.CreateDate, expression => expression.Ignore()) + .ForMember(type => type.UpdateDate, expression => expression.Ignore()) + .ForMember(type => type.DeletedDate, expression => expression.Ignore()) + .ForMember(type => type.HasIdentity, expression => expression.Ignore()); + */ + + CreateMap() + //do the base mapping + .MapBaseContentTypeSaveToEntity() + .ConstructUsing((source) => new ContentType(source.ParentId)) + .ForMember(source => source.AllowedTemplates, expression => expression.Ignore()) + .ForMember(dto => dto.DefaultTemplate, expression => expression.Ignore()) + .AfterMap((source, dest) => + { + dest.AllowedTemplates = source.AllowedTemplates + .Where(x => x != null) + .Select(s => _fileService.GetTemplate(s)) + .ToArray(); + + if (source.DefaultTemplate != null) + dest.SetDefaultTemplate(_fileService.GetTemplate(source.DefaultTemplate)); + + ContentTypeModelMapperExtensions.AfterMapContentTypeSaveToEntity(source, dest, _contentTypeService); + }); + + CreateMap() + //do the base mapping + .MapBaseContentTypeSaveToEntity() + .ConstructUsing((source) => new MediaType(source.ParentId)) + .AfterMap((source, dest) => + { + ContentTypeModelMapperExtensions.AfterMapMediaTypeSaveToEntity(source, dest, _mediaTypeService); + }); + + CreateMap() + //do the base mapping + .MapBaseContentTypeSaveToEntity() + .ConstructUsing((source) => new MemberType(source.ParentId)) + .AfterMap((source, dest) => + { + ContentTypeModelMapperExtensions.AfterMapContentTypeSaveToEntity(source, dest, _contentTypeService); + + //map the MemberCanEditProperty,MemberCanViewProperty + foreach (var propertyType in source.Groups.SelectMany(x => x.Properties)) + { + var localCopy = propertyType; + var destProp = dest.PropertyTypes.SingleOrDefault(x => x.Alias.InvariantEquals(localCopy.Alias)); + if (destProp != null) + { + dest.SetMemberCanEditProperty(localCopy.Alias, localCopy.MemberCanEditProperty); + dest.SetMemberCanViewProperty(localCopy.Alias, localCopy.MemberCanViewProperty); + } + } + }); + + CreateMap().ConvertUsing(x => x.Alias); + + CreateMap() + //map base logic + .MapBaseContentTypeEntityToDisplay(_propertyEditors, _dataTypeService, _contentTypeService) + .AfterMap((memberType, display) => + { + //map the MemberCanEditProperty,MemberCanViewProperty + foreach (var propertyType in memberType.PropertyTypes) + { + var localCopy = propertyType; + var displayProp = display.Groups.SelectMany(x => x.Properties).SingleOrDefault(x => x.Alias.InvariantEquals(localCopy.Alias)); + if (displayProp != null) + { + displayProp.MemberCanEditProperty = memberType.MemberCanEditProperty(localCopy.Alias); + displayProp.MemberCanViewProperty = memberType.MemberCanViewProperty(localCopy.Alias); + } + } + }); + + CreateMap() + //map base logic + .MapBaseContentTypeEntityToDisplay(_propertyEditors, _dataTypeService, _contentTypeService) + .AfterMap((source, dest) => + { + //default listview + dest.ListViewEditorName = Constants.Conventions.DataTypes.ListViewPrefix + "Media"; + + if (string.IsNullOrEmpty(source.Name) == false) + { + var name = Constants.Conventions.DataTypes.ListViewPrefix + source.Name; + if (_dataTypeService.GetDataTypeDefinitionByName(name) != null) + dest.ListViewEditorName = name; + } + }); + + CreateMap() + //map base logic + .MapBaseContentTypeEntityToDisplay(_propertyEditors, _dataTypeService, _contentTypeService) + .ForMember(dto => dto.AllowedTemplates, expression => expression.Ignore()) + .ForMember(dto => dto.DefaultTemplate, expression => expression.Ignore()) + .ForMember(display => display.Notifications, expression => expression.Ignore()) + .AfterMap((source, dest) => + { + //sync templates + dest.AllowedTemplates = source.AllowedTemplates.Select(Mapper.Map).ToArray(); + + if (source.DefaultTemplate != null) + dest.DefaultTemplate = Mapper.Map(source.DefaultTemplate); + + //default listview + dest.ListViewEditorName = Constants.Conventions.DataTypes.ListViewPrefix + "Content"; + + if (string.IsNullOrEmpty(source.Alias) == false) + { + var name = Constants.Conventions.DataTypes.ListViewPrefix + source.Alias; + if (_dataTypeService.GetDataTypeDefinitionByName(name) != null) + dest.ListViewEditorName = name; + } + + }); + + CreateMap() + .ForMember(x => x.Udi, expression => expression.MapFrom(content => Udi.Create(Constants.UdiEntityType.MemberType, content.Key))); + CreateMap() + .ForMember(x => x.Udi, expression => expression.MapFrom(content => Udi.Create(Constants.UdiEntityType.MediaType, content.Key))); + CreateMap() + .ForMember(x => x.Udi, expression => expression.MapFrom(content => Udi.Create(Constants.UdiEntityType.DocumentType, content.Key))); + + CreateMap() + + .ConstructUsing(propertyTypeBasic => + { + var dataType = _dataTypeService.GetDataTypeDefinitionById(propertyTypeBasic.DataTypeId); + if (dataType == null) throw new NullReferenceException("No data type found with id " + propertyTypeBasic.DataTypeId); + return new PropertyType(dataType, propertyTypeBasic.Alias); + }) + + // see note above - have to do this here? + .ForMember(type => type.PropertyEditorAlias, expression => expression.Ignore()) + .ForMember(type => type.DeletedDate, expression => expression.Ignore()) + + //only map if it is actually set + .ForMember(dest => dest.Id, expression => expression.Condition(source => source.Id > 0)) + .ForMember(dto => dto.CreateDate, expression => expression.Ignore()) + .ForMember(dto => dto.UpdateDate, expression => expression.Ignore()) + //only map if it is actually set, if it's not set, it needs to be handled differently and will be taken care of in the + // IContentType.AddPropertyType + .ForMember(dest => dest.PropertyGroupId, expression => expression.Condition(source => source.GroupId > 0)) + .ForMember(type => type.PropertyGroupId, expression => expression.MapFrom(display => new Lazy(() => display.GroupId, false))) + .ForMember(type => type.Key, expression => expression.Ignore()) + .ForMember(type => type.HelpText, expression => expression.Ignore()) + .ForMember(type => type.HasIdentity, expression => expression.Ignore()) + //ignore because this is set in the ctor NOT ON UPDATE, STUPID! + //.ForMember(type => type.Alias, expression => expression.Ignore()) + //ignore because this is obsolete and shouldn't be used + .ForMember(type => type.DataTypeId, expression => expression.Ignore()) + .ForMember(type => type.Mandatory, expression => expression.MapFrom(display => display.Validation.Mandatory)) + .ForMember(type => type.ValidationRegExp, expression => expression.MapFrom(display => display.Validation.Pattern)) + .ForMember(type => type.DataTypeDefinitionId, expression => expression.MapFrom(display => display.DataTypeId)) + .ForMember(type => type.Name, expression => expression.MapFrom(display => display.Label)); + + #region *** Used for mapping on top of an existing display object from a save object *** + + CreateMap() + .MapBaseContentTypeSaveToDisplay(); + + CreateMap() + .MapBaseContentTypeSaveToDisplay(); + + CreateMap() + .MapBaseContentTypeSaveToDisplay() + .ForMember(dto => dto.AllowedTemplates, expression => expression.Ignore()) + .ForMember(dto => dto.DefaultTemplate, expression => expression.Ignore()) + .AfterMap((source, dest) => + { + //sync templates + var destAllowedTemplateAliases = dest.AllowedTemplates.Select(x => x.Alias); + //if the dest is set and it's the same as the source, then don't change + if (destAllowedTemplateAliases.SequenceEqual(source.AllowedTemplates) == false) + { + var templates = _fileService.GetTemplates(source.AllowedTemplates.ToArray()); + dest.AllowedTemplates = source.AllowedTemplates + .Select(x => Mapper.Map(templates.SingleOrDefault(t => t.Alias == x))) + .WhereNotNull() + .ToArray(); + } + + if (source.DefaultTemplate.IsNullOrWhiteSpace() == false) + { + //if the dest is set and it's the same as the source, then don't change + if (dest.DefaultTemplate == null || source.DefaultTemplate != dest.DefaultTemplate.Alias) + { + var template = _fileService.GetTemplate(source.DefaultTemplate); + dest.DefaultTemplate = template == null ? null : Mapper.Map(template); + } + } + else + { + dest.DefaultTemplate = null; + } + }); + + //for doc types, media types + CreateMap, PropertyGroup>() + .MapPropertyGroupBasicToPropertyGroupPersistence, PropertyTypeBasic>(); + + //for members + CreateMap, PropertyGroup>() + .MapPropertyGroupBasicToPropertyGroupPersistence, MemberPropertyTypeBasic>(); + + //for doc types, media types + CreateMap, PropertyGroupDisplay>() + .MapPropertyGroupBasicToPropertyGroupDisplay, PropertyTypeBasic, PropertyTypeDisplay>(); + + //for members + CreateMap, PropertyGroupDisplay>() + .MapPropertyGroupBasicToPropertyGroupDisplay, MemberPropertyTypeBasic, MemberPropertyTypeDisplay>(); + + CreateMap() + .ForMember(g => g.Editor, expression => expression.Ignore()) + .ForMember(g => g.View, expression => expression.Ignore()) + .ForMember(g => g.Config, expression => expression.Ignore()) + .ForMember(g => g.ContentTypeId, expression => expression.Ignore()) + .ForMember(g => g.ContentTypeName, expression => expression.Ignore()) + .ForMember(g => g.Locked, exp => exp.Ignore()); + + CreateMap() + .ForMember(g => g.Editor, expression => expression.Ignore()) + .ForMember(g => g.View, expression => expression.Ignore()) + .ForMember(g => g.Config, expression => expression.Ignore()) + .ForMember(g => g.ContentTypeId, expression => expression.Ignore()) + .ForMember(g => g.ContentTypeName, expression => expression.Ignore()) + .ForMember(g => g.Locked, exp => exp.Ignore()); + + #endregion + + + + + } + + + } } \ No newline at end of file diff --git a/src/Umbraco.Web/Models/Mapping/ContentTypeUdiResolver.cs b/src/Umbraco.Web/Models/Mapping/ContentTypeUdiResolver.cs index 2d33b17155..dd57c9c317 100644 --- a/src/Umbraco.Web/Models/Mapping/ContentTypeUdiResolver.cs +++ b/src/Umbraco.Web/Models/Mapping/ContentTypeUdiResolver.cs @@ -1,4 +1,3 @@ -using AutoMapper; using Umbraco.Core; using Umbraco.Core.Models; @@ -7,9 +6,9 @@ namespace Umbraco.Web.Models.Mapping /// /// Resolves a UDI for a content type based on it's type /// - internal class ContentTypeUdiResolver : ValueResolver + internal class ContentTypeUdiResolver { - protected override Udi ResolveCore(IContentTypeComposition source) + public Udi Resolve(IContentTypeComposition source) { if (source == null) return null; diff --git a/src/Umbraco.Web/Models/Mapping/CreatorResolver.cs b/src/Umbraco.Web/Models/Mapping/CreatorResolver.cs index 6f0f5bde77..fc5291bca6 100644 --- a/src/Umbraco.Web/Models/Mapping/CreatorResolver.cs +++ b/src/Umbraco.Web/Models/Mapping/CreatorResolver.cs @@ -9,7 +9,7 @@ namespace Umbraco.Web.Models.Mapping /// /// Maps the Creator for content /// - internal class CreatorResolver : ValueResolver + internal class CreatorResolver { private readonly IUserService _userService; @@ -18,7 +18,7 @@ namespace Umbraco.Web.Models.Mapping _userService = userService; } - protected override UserBasic ResolveCore(IContent source) + public UserBasic Resolve(IContent source) { return Mapper.Map(source.GetWriterProfile(_userService)); } diff --git a/src/Umbraco.Web/Models/Mapping/DashboardModelsMapper.cs b/src/Umbraco.Web/Models/Mapping/DashboardProfile.cs similarity index 71% rename from src/Umbraco.Web/Models/Mapping/DashboardModelsMapper.cs rename to src/Umbraco.Web/Models/Mapping/DashboardProfile.cs index 1ad2fa39e5..f23e6def40 100644 --- a/src/Umbraco.Web/Models/Mapping/DashboardModelsMapper.cs +++ b/src/Umbraco.Web/Models/Mapping/DashboardProfile.cs @@ -1,5 +1,4 @@ using AutoMapper; -using Umbraco.Core.Models.Mapping; using Umbraco.Web.Models.ContentEditing; using Umbraco.Core.Models; @@ -8,11 +7,11 @@ namespace Umbraco.Web.Models.Mapping /// /// A model mapper used to map models for the various dashboards /// - internal class DashboardModelsMapper : ModelMapperConfiguration + internal class DashboardProfile : Profile { - public override void ConfigureMappings(IMapperConfiguration config) + public DashboardProfile() { - config.CreateMap() + CreateMap() .ForMember(x => x.OriginalUrl, expression => expression.MapFrom(item => UmbracoContext.Current.UrlProvider.GetUrlFromRoute(item.ContentId, item.Url))) .ForMember(x => x.DestinationUrl, expression => expression.Ignore()) .ForMember(x => x.RedirectId, expression => expression.MapFrom(item => item.Key)); diff --git a/src/Umbraco.Web/Models/Mapping/DataTypeModelMapper.cs b/src/Umbraco.Web/Models/Mapping/DataTypeModelMapper.cs deleted file mode 100644 index 777d70e437..0000000000 --- a/src/Umbraco.Web/Models/Mapping/DataTypeModelMapper.cs +++ /dev/null @@ -1,135 +0,0 @@ -using System; -using System.Collections.Generic; -using AutoMapper; -using System.Linq; -using Umbraco.Core; -using Umbraco.Core.Configuration; -using Umbraco.Core.Models; -using Umbraco.Core.Models.Mapping; -using Umbraco.Core.PropertyEditors; -using Umbraco.Core.Services; -using Umbraco.Web.Composing; -using Umbraco.Web.Models.ContentEditing; - -namespace Umbraco.Web.Models.Mapping -{ - /// - /// Configure's model mappings for Data types - /// - internal class DataTypeModelMapper : ModelMapperConfiguration - { - private readonly IDataTypeService _dataTypeService; - - public DataTypeModelMapper(IDataTypeService dataTypeService) - { - _dataTypeService = dataTypeService; - } - - public override void ConfigureMappings(IMapperConfiguration config) - { - var lazyDataTypeService = new Lazy(() => _dataTypeService); - - config.CreateMap(); - - //just maps the standard properties, does not map the value! - config.CreateMap() - .ForMember(x => x.Value, expression => expression.Ignore()); - - var systemIds = new[] - { - Constants.DataTypes.DefaultContentListView, - Constants.DataTypes.DefaultMediaListView, - Constants.DataTypes.DefaultMembersListView - }; - - config.CreateMap() - .ForMember(x => x.Udi, expression => expression.Ignore()) - .ForMember(x => x.HasPrevalues, expression => expression.Ignore()) - .ForMember(x => x.IsSystemDataType, expression => expression.Ignore()) - .ForMember(x => x.Id, expression => expression.Ignore()) - .ForMember(x => x.Trashed, expression => expression.Ignore()) - .ForMember(x => x.Key, expression => expression.Ignore()) - .ForMember(x => x.ParentId, expression => expression.Ignore()) - .ForMember(x => x.Path, expression => expression.Ignore()) - .ForMember(x => x.AdditionalData, expression => expression.Ignore()); - - config.CreateMap() - .ForMember(x => x.Udi, expression => expression.MapFrom(content => Udi.Create(Constants.UdiEntityType.DataType, content.Key))) - .ForMember(x => x.HasPrevalues, expression => expression.Ignore()) - .ForMember(x => x.Icon, expression => expression.Ignore()) - .ForMember(x => x.Alias, expression => expression.Ignore()) - .ForMember(x => x.Group, expression => expression.Ignore()) - .ForMember(x => x.IsSystemDataType, expression => expression.MapFrom(definition => systemIds.Contains(definition.Id))) - .AfterMap((def, basic) => - { - var editor = Current.PropertyEditors[def.PropertyEditorAlias]; - if (editor != null) - { - basic.Alias = editor.Alias; - basic.Group = editor.Group; - basic.Icon = editor.Icon; - } - }); - - config.CreateMap() - .ForMember(x => x.Udi, expression => expression.MapFrom(content => Udi.Create(Constants.UdiEntityType.DataType, content.Key))) - .ForMember(display => display.AvailableEditors, expression => expression.ResolveUsing(new AvailablePropertyEditorsResolver(UmbracoConfig.For.UmbracoSettings().Content))) - .ForMember(display => display.PreValues, expression => expression.ResolveUsing(new PreValueDisplayResolver(lazyDataTypeService))) - .ForMember(display => display.SelectedEditor, expression => expression.MapFrom( - definition => definition.PropertyEditorAlias.IsNullOrWhiteSpace() ? null : definition.PropertyEditorAlias)) - .ForMember(x => x.HasPrevalues, expression => expression.Ignore()) - .ForMember(x => x.Notifications, expression => expression.Ignore()) - .ForMember(x => x.Icon, expression => expression.Ignore()) - .ForMember(x => x.Alias, expression => expression.Ignore()) - .ForMember(x => x.Group, expression => expression.Ignore()) - .ForMember(x => x.IsSystemDataType, expression => expression.MapFrom(definition => systemIds.Contains(definition.Id))) - .AfterMap((def, basic) => - { - var editor = Current.PropertyEditors[def.PropertyEditorAlias]; - if (editor != null) - { - basic.Group = editor.Group; - basic.Icon = editor.Icon; - } - }); - - //gets a list of PreValueFieldDisplay objects from the data type definition - config.CreateMap>() - .ConvertUsing(definition => - { - var resolver = new PreValueDisplayResolver(lazyDataTypeService); - return resolver.Convert(definition); - }); - - config.CreateMap() - .ConstructUsing(save => new DataTypeDefinition(save.SelectedEditor) {CreateDate = DateTime.Now}) - .ForMember(definition => definition.Id, expression => expression.MapFrom(save => Convert.ToInt32(save.Id))) - //we have to ignore the Key otherwise this will reset the UniqueId field which should never change! - // http://issues.umbraco.org/issue/U4-3911 - .ForMember(definition => definition.Key, expression => expression.Ignore()) - .ForMember(definition => definition.Path, expression => expression.Ignore()) - .ForMember(definition => definition.PropertyEditorAlias, expression => expression.MapFrom(save => save.SelectedEditor)) - .ForMember(definition => definition.DatabaseType, expression => expression.ResolveUsing(new DatabaseTypeResolver())) - .ForMember(x => x.CreatorId, expression => expression.Ignore()) - .ForMember(x => x.Level, expression => expression.Ignore()) - .ForMember(x => x.SortOrder, expression => expression.Ignore()) - .ForMember(x => x.CreateDate, expression => expression.Ignore()) - .ForMember(x => x.DeletedDate, expression => expression.Ignore()) - .ForMember(x => x.UpdateDate, expression => expression.Ignore()); - - //Converts a property editor to a new list of pre-value fields - used when creating a new data type or changing a data type with new pre-vals - config.CreateMap>() - .ConvertUsing(editor => - { - //this is a new data type, so just return the field editors, there are no values yet - var defaultVals = editor.DefaultPreValues; - var fields = editor.PreValueEditor.Fields.Select(Mapper.Map).ToArray(); - if (defaultVals != null) - { - PreValueDisplayResolver.MapPreValueValuesToPreValueFields(fields, defaultVals); - } - return fields; - }); - } - } -} \ No newline at end of file diff --git a/src/Umbraco.Web/Models/Mapping/DataTypeProfile.cs b/src/Umbraco.Web/Models/Mapping/DataTypeProfile.cs new file mode 100644 index 0000000000..35e45b32d0 --- /dev/null +++ b/src/Umbraco.Web/Models/Mapping/DataTypeProfile.cs @@ -0,0 +1,127 @@ +using System; +using System.Collections.Generic; +using AutoMapper; +using System.Linq; +using Umbraco.Core; +using Umbraco.Core.Configuration; +using Umbraco.Core.Models; +using Umbraco.Core.PropertyEditors; +using Umbraco.Core.Services; +using Umbraco.Web.Composing; +using Umbraco.Web.Models.ContentEditing; + +namespace Umbraco.Web.Models.Mapping +{ + /// + /// Configure's model mappings for Data types + /// + internal class DataTypeProfile : Profile + { + public DataTypeProfile(IDataTypeService dataTypeService) + { + var lazyDataTypeService = new Lazy(() => dataTypeService); + + // create, capture, cache + var availablePropertyEditorsResolver = new AvailablePropertyEditorsResolver(UmbracoConfig.For.UmbracoSettings().Content); + var preValueDisplayResolver = new PreValueDisplayResolver(lazyDataTypeService); + var databaseTypeResolver = new DatabaseTypeResolver(); + + CreateMap(); + + //just maps the standard properties, does not map the value! + CreateMap() + .ForMember(dest => dest.Value, opt => opt.Ignore()); + + var systemIds = new[] + { + Constants.DataTypes.DefaultContentListView, + Constants.DataTypes.DefaultMediaListView, + Constants.DataTypes.DefaultMembersListView + }; + + CreateMap() + .ForMember(dest => dest.Udi, opt => opt.Ignore()) + .ForMember(dest => dest.HasPrevalues, opt => opt.Ignore()) + .ForMember(dest => dest.IsSystemDataType, opt => opt.Ignore()) + .ForMember(dest => dest.Id, opt => opt.Ignore()) + .ForMember(dest => dest.Trashed, opt => opt.Ignore()) + .ForMember(dest => dest.Key, opt => opt.Ignore()) + .ForMember(dest => dest.ParentId, opt => opt.Ignore()) + .ForMember(dest => dest.Path, opt => opt.Ignore()) + .ForMember(dest => dest.AdditionalData, opt => opt.Ignore()); + + CreateMap() + .ForMember(dest => dest.Udi, opt => opt.MapFrom(src => Udi.Create(Constants.UdiEntityType.DataType, src.Key))) + .ForMember(dest => dest.HasPrevalues, opt => opt.Ignore()) + .ForMember(dest => dest.Icon, opt => opt.Ignore()) + .ForMember(dest => dest.Alias, opt => opt.Ignore()) + .ForMember(dest => dest.Group, opt => opt.Ignore()) + .ForMember(dest => dest.IsSystemDataType, opt => opt.MapFrom(src => systemIds.Contains(src.Id))) + .AfterMap((src, dest) => + { + var editor = Current.PropertyEditors[src.PropertyEditorAlias]; + if (editor != null) + { + dest.Alias = editor.Alias; + dest.Group = editor.Group; + dest.Icon = editor.Icon; + } + }); + + CreateMap() + .ForMember(dest => dest.Udi, opt => opt.MapFrom(src => Udi.Create(Constants.UdiEntityType.DataType, src.Key))) + .ForMember(dest => dest.AvailableEditors, opt => opt.ResolveUsing(src => availablePropertyEditorsResolver.Resolve(src))) + .ForMember(dest => dest.PreValues, opt => opt.ResolveUsing(src => preValueDisplayResolver.Resolve(src))) + .ForMember(dest => dest.SelectedEditor, opt => opt.MapFrom(src => src.PropertyEditorAlias.IsNullOrWhiteSpace() ? null : src.PropertyEditorAlias)) + .ForMember(dest => dest.HasPrevalues, opt => opt.Ignore()) + .ForMember(dest => dest.Notifications, opt => opt.Ignore()) + .ForMember(dest => dest.Icon, opt => opt.Ignore()) + .ForMember(dest => dest.Alias, opt => opt.Ignore()) + .ForMember(dest => dest.Group, opt => opt.Ignore()) + .ForMember(dest => dest.IsSystemDataType, opt => opt.MapFrom(src => systemIds.Contains(src.Id))) + .AfterMap((src, dest) => + { + var editor = Current.PropertyEditors[src.PropertyEditorAlias]; + if (editor != null) + { + dest.Group = editor.Group; + dest.Icon = editor.Icon; + } + }); + + //gets a list of PreValueFieldDisplay objects from the data type definition + CreateMap>() + .ConvertUsing(src => preValueDisplayResolver.Resolve(src)); + + CreateMap() + .ConstructUsing(src => new DataTypeDefinition(src.SelectedEditor) {CreateDate = DateTime.Now}) + .ForMember(dest => dest.Id, opt => opt.MapFrom(src => Convert.ToInt32(src.Id))) + //we have to ignore the Key otherwise this will reset the UniqueId field which should never change! + // http://issues.umbraco.org/issue/U4-3911 + .ForMember(dest => dest.Key, opt => opt.Ignore()) + .ForMember(dest => dest.Path, opt => opt.Ignore()) + .ForMember(dest => dest.PropertyEditorAlias, opt => opt.MapFrom(src => src.SelectedEditor)) + .ForMember(dest => dest.DatabaseType, opt => opt.ResolveUsing(src => databaseTypeResolver.Resolve(src))) + .ForMember(dest => dest.CreatorId, opt => opt.Ignore()) + .ForMember(dest => dest.Level, opt => opt.Ignore()) + .ForMember(dest => dest.SortOrder, opt => opt.Ignore()) + .ForMember(dest => dest.CreateDate, opt => opt.Ignore()) + .ForMember(dest => dest.DeletedDate, opt => opt.Ignore()) + .ForMember(dest => dest.UpdateDate, opt => opt.Ignore()); + + //Converts a property editor to a new list of pre-value fields - used when creating a new data type or changing a data type with new pre-vals + CreateMap>() + .ConvertUsing(src => + { + //this is a new data type, so just return the field editors, there are no values yet + var defaultVals = src.DefaultPreValues; + var fields = src.PreValueEditor.Fields.Select(Mapper.Map).ToArray(); + if (defaultVals != null) + { + PreValueDisplayResolver.MapPreValueValuesToPreValueFields(fields, defaultVals); + } + return fields; + }); + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Web/Models/Mapping/DatabaseTypeResolver.cs b/src/Umbraco.Web/Models/Mapping/DatabaseTypeResolver.cs index 73362b5030..d456067655 100644 --- a/src/Umbraco.Web/Models/Mapping/DatabaseTypeResolver.cs +++ b/src/Umbraco.Web/Models/Mapping/DatabaseTypeResolver.cs @@ -1,7 +1,5 @@ using System; -using AutoMapper; using Umbraco.Core.Models; -using Umbraco.Core.PropertyEditors; using Umbraco.Web.Composing; using Umbraco.Web.Models.ContentEditing; @@ -10,9 +8,9 @@ namespace Umbraco.Web.Models.Mapping /// /// Gets the DataTypeDatabaseType from the selected property editor for the data type /// - internal class DatabaseTypeResolver : ValueResolver + internal class DatabaseTypeResolver { - protected override DataTypeDatabaseType ResolveCore(DataTypeSave source) + public DataTypeDatabaseType Resolve(DataTypeSave source) { var propertyEditor = Current.PropertyEditors[source.SelectedEditor]; if (propertyEditor == null) diff --git a/src/Umbraco.Web/Models/Mapping/EntityModelMapper.cs b/src/Umbraco.Web/Models/Mapping/EntityModelMapper.cs deleted file mode 100644 index 90f96f14ef..0000000000 --- a/src/Umbraco.Web/Models/Mapping/EntityModelMapper.cs +++ /dev/null @@ -1,167 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using AutoMapper; -using Examine; -using Examine.LuceneEngine.Providers; -using Examine.LuceneEngine; -using Examine.LuceneEngine.Providers; -using Umbraco.Core; -using Umbraco.Core.Models; -using Umbraco.Core.Models.Mapping; -using Umbraco.Core.Models.Membership; -using Umbraco.Web.Models.ContentEditing; -using UmbracoExamine; - -namespace Umbraco.Web.Models.Mapping -{ - internal class EntityModelMapper : ModelMapperConfiguration - { - public override void ConfigureMappings(IMapperConfiguration config) - { - config.CreateMap() - .ForMember(x => x.Udi, expression => expression.MapFrom(x => Udi.Create(UmbracoObjectTypesExtensions.GetUdiType(x.NodeObjectTypeId), x.Key))) - .ForMember(basic => basic.Icon, expression => expression.MapFrom(entity => entity.ContentTypeIcon)) - .ForMember(dto => dto.Trashed, expression => expression.Ignore()) - .ForMember(x => x.Alias, expression => expression.Ignore()) - .AfterMap((entity, basic) => - { - if (entity.NodeObjectTypeId == Constants.ObjectTypes.MemberGuid && basic.Icon.IsNullOrWhiteSpace()) - { - basic.Icon = "icon-user"; - } - }); - - config.CreateMap() - .ForMember(x => x.Udi, expression => expression.Ignore()) - .ForMember(basic => basic.Icon, expression => expression.UseValue("icon-box")) - .ForMember(basic => basic.Path, expression => expression.UseValue("")) - .ForMember(basic => basic.ParentId, expression => expression.UseValue(-1)) - .ForMember(dto => dto.Trashed, expression => expression.Ignore()) - .ForMember(x => x.AdditionalData, expression => expression.Ignore()); - - config.CreateMap() - .ForMember(x => x.Udi, expression => expression.Ignore()) - .ForMember(basic => basic.Icon, expression => expression.UseValue("icon-tab")) - .ForMember(basic => basic.Path, expression => expression.UseValue("")) - .ForMember(basic => basic.ParentId, expression => expression.UseValue(-1)) - //in v6 the 'alias' is it's lower cased name so we'll stick to that. - .ForMember(basic => basic.Alias, expression => expression.MapFrom(group => group.Name.ToLowerInvariant())) - .ForMember(dto => dto.Trashed, expression => expression.Ignore()) - .ForMember(x => x.AdditionalData, expression => expression.Ignore()); - - config.CreateMap() - .ForMember(x => x.Udi, expression => expression.Ignore()) - .ForMember(basic => basic.Icon, expression => expression.UseValue("icon-user")) - .ForMember(basic => basic.Path, expression => expression.UseValue("")) - .ForMember(basic => basic.ParentId, expression => expression.UseValue(-1)) - .ForMember(basic => basic.Alias, expression => expression.MapFrom(user => user.Username)) - .ForMember(dto => dto.Trashed, expression => expression.Ignore()) - .ForMember(x => x.AdditionalData, expression => expression.Ignore()); - - config.CreateMap() - .ForMember(x => x.Udi, expression => expression.MapFrom(x => Udi.Create(Constants.UdiEntityType.Template, x.Key))) - .ForMember(basic => basic.Icon, expression => expression.UseValue("icon-layout")) - .ForMember(basic => basic.Path, expression => expression.MapFrom(template => template.Path)) - .ForMember(basic => basic.ParentId, expression => expression.UseValue(-1)) - .ForMember(dto => dto.Trashed, expression => expression.Ignore()) - .ForMember(x => x.AdditionalData, expression => expression.Ignore()); - - //config.CreateMap() - // .ConstructUsing(basic => new Template(basic.Name, basic.Alias) - // { - // Id = Convert.ToInt32(basic.Id), - // Key = basic.Key - // }) - // .ForMember(t => t.Path, expression => expression.Ignore()) - // .ForMember(t => t.Id, expression => expression.MapFrom(template => Convert.ToInt32(template.Id))) - // .ForMember(x => x.VirtualPath, expression => expression.Ignore()) - // .ForMember(x => x.CreateDate, expression => expression.Ignore()) - // .ForMember(x => x.UpdateDate, expression => expression.Ignore()) - // .ForMember(x => x.Content, expression => expression.Ignore()); - - config.CreateMap() - .ForMember(x => x.Id, expression => expression.MapFrom(entity => new Lazy(() => Convert.ToInt32(entity.Id)))) - .ForMember(x => x.SortOrder, expression => expression.Ignore()); - - config.CreateMap() - .ForMember(x => x.Udi, expression => expression.ResolveUsing(new ContentTypeUdiResolver())) - .ForMember(basic => basic.Path, expression => expression.MapFrom(x => x.Path)) - .ForMember(basic => basic.ParentId, expression => expression.MapFrom(x => x.ParentId)) - .ForMember(dto => dto.Trashed, expression => expression.Ignore()) - .ForMember(x => x.AdditionalData, expression => expression.Ignore()); - - config.CreateMap() - //default to document icon - .ForMember(x => x.Udi, expression => expression.Ignore()) - .ForMember(x => x.Icon, expression => expression.Ignore()) - .ForMember(x => x.Id, expression => expression.MapFrom(result => result.Id)) - .ForMember(x => x.Name, expression => expression.Ignore()) - .ForMember(x => x.Key, expression => expression.Ignore()) - .ForMember(x => x.ParentId, expression => expression.Ignore()) - .ForMember(x => x.Alias, expression => expression.Ignore()) - .ForMember(x => x.Path, expression => expression.Ignore()) - .ForMember(dto => dto.Trashed, expression => expression.Ignore()) - .ForMember(x => x.AdditionalData, expression => expression.Ignore()) - .AfterMap((result, basic) => - { - //get the icon if there is one - basic.Icon = result.Fields.ContainsKey(BaseUmbracoIndexer.IconFieldName) - ? result.Fields[BaseUmbracoIndexer.IconFieldName] - : "icon-document"; - - basic.Name = result.Fields.ContainsKey("nodeName") ? result.Fields["nodeName"] : "[no name]"; - if (result.Fields.ContainsKey(UmbracoContentIndexer.NodeKeyFieldName)) - { - Guid key; - if (Guid.TryParse(result.Fields[UmbracoContentIndexer.NodeKeyFieldName], out key)) - { - basic.Key = key; - - //need to set the UDI - if (result.Fields.ContainsKey(LuceneIndexer.IndexTypeFieldName)) - { - switch (result.Fields[LuceneIndexer.IndexTypeFieldName]) - { - case IndexTypes.Member: - basic.Udi = new GuidUdi(Constants.UdiEntityType.Member, basic.Key); - break; - case IndexTypes.Content: - basic.Udi = new GuidUdi(Constants.UdiEntityType.Document, basic.Key); - break; - case IndexTypes.Media: - basic.Udi = new GuidUdi(Constants.UdiEntityType.Media, basic.Key); - break; - } - } - } - } - - if (result.Fields.ContainsKey("parentID")) - { - int parentId; - if (int.TryParse(result.Fields["parentID"], out parentId)) - { - basic.ParentId = parentId; - } - else - { - basic.ParentId = -1; - } - } - basic.Path = result.Fields.ContainsKey(UmbracoContentIndexer.IndexPathFieldName) ? result.Fields[UmbracoContentIndexer.IndexPathFieldName] : ""; - - if (result.Fields.ContainsKey(LuceneIndexer.NodeTypeAliasFieldName)) - { - basic.AdditionalData.Add("contentType", result.Fields[LuceneIndexer.NodeTypeAliasFieldName]); - } - }); - - config.CreateMap>() - .ConvertUsing(results => results.Select(Mapper.Map).ToList()); - - config.CreateMap, IEnumerable>() - .ConvertUsing(results => results.Select(Mapper.Map).ToList()); - } - } -} \ No newline at end of file diff --git a/src/Umbraco.Web/Models/Mapping/EntityProfile.cs b/src/Umbraco.Web/Models/Mapping/EntityProfile.cs new file mode 100644 index 0000000000..422cfd225d --- /dev/null +++ b/src/Umbraco.Web/Models/Mapping/EntityProfile.cs @@ -0,0 +1,168 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using AutoMapper; +using Examine; +using Examine.LuceneEngine.Providers; +using Examine.LuceneEngine; +using Umbraco.Core; +using Umbraco.Core.Models; +using Umbraco.Core.Models.Membership; +using Umbraco.Web.Models.ContentEditing; +using UmbracoExamine; + +namespace Umbraco.Web.Models.Mapping +{ + internal class EntityProfile : Profile + { + public EntityProfile() + { + // create, capture, cache + var contentTypeUdiResolver = new ContentTypeUdiResolver(); + + CreateMap() + .ForMember(dest => dest.Udi, opt => opt.MapFrom(src => Udi.Create(UmbracoObjectTypesExtensions.GetUdiType(src.NodeObjectTypeId), src.Key))) + .ForMember(dest => dest.Icon, opt => opt.MapFrom(src => src.ContentTypeIcon)) + .ForMember(dest => dest.Trashed, opt => opt.Ignore()) + .ForMember(dest => dest.Alias, opt => opt.Ignore()) + .AfterMap((src, dest) => + { + if (src.NodeObjectTypeId == Constants.ObjectTypes.MemberGuid && dest.Icon.IsNullOrWhiteSpace()) + { + dest.Icon = "icon-user"; + } + }); + + CreateMap() + .ForMember(dest => dest.Udi, opt => opt.Ignore()) + .ForMember(dest => dest.Icon, opt => opt.UseValue("icon-box")) + .ForMember(dest => dest.Path, opt => opt.UseValue("")) + .ForMember(dest => dest.ParentId, opt => opt.UseValue(-1)) + .ForMember(dest => dest.Trashed, opt => opt.Ignore()) + .ForMember(dest => dest.AdditionalData, opt => opt.Ignore()); + + CreateMap() + .ForMember(dest => dest.Udi, opt => opt.Ignore()) + .ForMember(dest => dest.Icon, opt => opt.UseValue("icon-tab")) + .ForMember(dest => dest.Path, opt => opt.UseValue("")) + .ForMember(dest => dest.ParentId, opt => opt.UseValue(-1)) + //in v6 the 'alias' is it's lower cased name so we'll stick to that. + .ForMember(dest => dest.Alias, opt => opt.MapFrom(src => src.Name.ToLowerInvariant())) + .ForMember(dest => dest.Trashed, opt => opt.Ignore()) + .ForMember(dest => dest.AdditionalData, opt => opt.Ignore()); + + CreateMap() + .ForMember(dest => dest.Udi, opt => opt.Ignore()) + .ForMember(dest => dest.Icon, opt => opt.UseValue("icon-user")) + .ForMember(dest => dest.Path, opt => opt.UseValue("")) + .ForMember(dest => dest.ParentId, opt => opt.UseValue(-1)) + .ForMember(dest => dest.Alias, opt => opt.MapFrom(src => src.Username)) + .ForMember(dest => dest.Trashed, opt => opt.Ignore()) + .ForMember(dest => dest.AdditionalData, opt => opt.Ignore()); + + CreateMap() + .ForMember(dest => dest.Udi, opt => opt.MapFrom(src => Udi.Create(Constants.UdiEntityType.Template, src.Key))) + .ForMember(dest => dest.Icon, opt => opt.UseValue("icon-layout")) + .ForMember(dest => dest.Path, opt => opt.MapFrom(src => src.Path)) + .ForMember(dest => dest.ParentId, opt => opt.UseValue(-1)) + .ForMember(dest => dest.Trashed, opt => opt.Ignore()) + .ForMember(dest => dest.AdditionalData, opt => opt.Ignore()); + + //CreateMap() + // .ConstructUsing(basic => new Template(basic.Name, basic.Alias) + // { + // Id = Convert.ToInt32(basic.Id), + // Key = basic.Key + // }) + // .ForMember(t => t.Path, opt => opt.Ignore()) + // .ForMember(t => t.Id, opt => opt.MapFrom(template => Convert.ToInt32(template.Id))) + // .ForMember(dest => dest.VirtualPath, opt => opt.Ignore()) + // .ForMember(dest => dest.CreateDate, opt => opt.Ignore()) + // .ForMember(dest => dest.UpdateDate, opt => opt.Ignore()) + // .ForMember(dest => dest.Content, opt => opt.Ignore()); + + CreateMap() + .ForMember(dest => dest.Id, opt => opt.MapFrom(src => new Lazy(() => Convert.ToInt32(src.Id)))) + .ForMember(dest => dest.SortOrder, opt => opt.Ignore()); + + CreateMap() + .ForMember(dest => dest.Udi, opt => opt.ResolveUsing(src => contentTypeUdiResolver.Resolve(src))) + .ForMember(dest => dest.Path, opt => opt.MapFrom(src => src.Path)) + .ForMember(dest => dest.ParentId, opt => opt.MapFrom(src => src.ParentId)) + .ForMember(dest => dest.Trashed, opt => opt.Ignore()) + .ForMember(dest => dest.AdditionalData, opt => opt.Ignore()); + + CreateMap() + //default to document icon + .ForMember(dest => dest.Udi, opt => opt.Ignore()) + .ForMember(dest => dest.Icon, opt => opt.Ignore()) + .ForMember(dest => dest.Id, opt => opt.MapFrom(src => src.Id)) + .ForMember(dest => dest.Name, opt => opt.Ignore()) + .ForMember(dest => dest.Key, opt => opt.Ignore()) + .ForMember(dest => dest.ParentId, opt => opt.Ignore()) + .ForMember(dest => dest.Alias, opt => opt.Ignore()) + .ForMember(dest => dest.Path, opt => opt.Ignore()) + .ForMember(dest => dest.Trashed, opt => opt.Ignore()) + .ForMember(dest => dest.AdditionalData, opt => opt.Ignore()) + .AfterMap((src, dest) => + { + //get the icon if there is one + dest.Icon = src.Fields.ContainsKey(BaseUmbracoIndexer.IconFieldName) + ? src.Fields[BaseUmbracoIndexer.IconFieldName] + : "icon-document"; + + dest.Name = src.Fields.ContainsKey("nodeName") ? src.Fields["nodeName"] : "[no name]"; + if (src.Fields.ContainsKey(UmbracoContentIndexer.NodeKeyFieldName)) + { + Guid key; + if (Guid.TryParse(src.Fields[UmbracoContentIndexer.NodeKeyFieldName], out key)) + { + dest.Key = key; + + //need to set the UDI + if (src.Fields.ContainsKey(LuceneIndexer.IndexTypeFieldName)) + { + switch (src.Fields[LuceneIndexer.IndexTypeFieldName]) + { + case IndexTypes.Member: + dest.Udi = new GuidUdi(Constants.UdiEntityType.Member, dest.Key); + break; + case IndexTypes.Content: + dest.Udi = new GuidUdi(Constants.UdiEntityType.Document, dest.Key); + break; + case IndexTypes.Media: + dest.Udi = new GuidUdi(Constants.UdiEntityType.Media, dest.Key); + break; + } + } + } + } + + if (src.Fields.ContainsKey("parentID")) + { + int parentId; + if (int.TryParse(src.Fields["parentID"], out parentId)) + { + dest.ParentId = parentId; + } + else + { + dest.ParentId = -1; + } + } + dest.Path = src.Fields.ContainsKey(UmbracoContentIndexer.IndexPathFieldName) ? src.Fields[UmbracoContentIndexer.IndexPathFieldName] : ""; + + if (src.Fields.ContainsKey(LuceneIndexer.NodeTypeAliasFieldName)) + { + dest.AdditionalData.Add("contentType", src.Fields[LuceneIndexer.NodeTypeAliasFieldName]); + } + }); + + CreateMap>() + .ConvertUsing(results => results.Select(Mapper.Map).ToList()); + + CreateMap, IEnumerable>() + .ConvertUsing(results => results.Select(Mapper.Map).ToList()); + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Web/Models/Mapping/LockedCompositionsResolver.cs b/src/Umbraco.Web/Models/Mapping/LockedCompositionsResolver.cs index c4335af02c..f1dcd2d40f 100644 --- a/src/Umbraco.Web/Models/Mapping/LockedCompositionsResolver.cs +++ b/src/Umbraco.Web/Models/Mapping/LockedCompositionsResolver.cs @@ -7,7 +7,7 @@ using Umbraco.Core.Services; namespace Umbraco.Web.Models.Mapping { - internal class LockedCompositionsResolver : ValueResolver> + internal class LockedCompositionsResolver { private readonly IContentTypeService _contentTypeService; @@ -16,7 +16,7 @@ namespace Umbraco.Web.Models.Mapping _contentTypeService = contentTypeService; } - protected override IEnumerable ResolveCore(IContentTypeComposition source) + public IEnumerable Resolve(IContentTypeComposition source) { var aliases = new List(); // get ancestor ids from path of parent if not root diff --git a/src/Umbraco.Web/Models/Mapping/MacroModelMapper.cs b/src/Umbraco.Web/Models/Mapping/MacroProfile.cs similarity index 78% rename from src/Umbraco.Web/Models/Mapping/MacroModelMapper.cs rename to src/Umbraco.Web/Models/Mapping/MacroProfile.cs index 5d6eab8600..66baacdd21 100644 --- a/src/Umbraco.Web/Models/Mapping/MacroModelMapper.cs +++ b/src/Umbraco.Web/Models/Mapping/MacroProfile.cs @@ -1,54 +1,53 @@ -using System.Collections.Generic; -using System.Linq; -using AutoMapper; -using Umbraco.Core; -using Umbraco.Core.Logging; -using Umbraco.Core.Models; -using Umbraco.Core.Models.Mapping; -using Umbraco.Web.Composing; -using Umbraco.Web.Models.ContentEditing; - -namespace Umbraco.Web.Models.Mapping -{ - /// - /// Declares model mappings for macros. - /// - internal class MacroModelMapper : ModelMapperConfiguration - { - public override void ConfigureMappings(IMapperConfiguration config) - { - //FROM IMacro TO EntityBasic - config.CreateMap() - .ForMember(x => x.Udi, expression => expression.MapFrom(content => Udi.Create(Constants.UdiEntityType.Macro, content.Key))) - .ForMember(entityBasic => entityBasic.Icon, expression => expression.UseValue("icon-settings-alt")) - .ForMember(dto => dto.ParentId, expression => expression.UseValue(-1)) - .ForMember(dto => dto.Path, expression => expression.ResolveUsing(macro => "-1," + macro.Id)) - .ForMember(dto => dto.Trashed, expression => expression.Ignore()) - .ForMember(dto => dto.AdditionalData, expression => expression.Ignore()); - - config.CreateMap>() - .ConvertUsing(macro => macro.Properties.Select(Mapper.Map).ToList()); - - config.CreateMap() - .ForMember(x => x.View, expression => expression.Ignore()) - .ForMember(x => x.Configuration, expression => expression.Ignore()) - .ForMember(x => x.Value, expression => expression.Ignore()) - .AfterMap((property, parameter) => - { - //map the view and the config - // we need to show the depracated ones for backwards compatibility - var paramEditor = Current.ParameterEditors[property.EditorAlias]; // fixme - include/filter deprecated?! - if (paramEditor == null) - { - //we'll just map this to a text box - paramEditor = Current.ParameterEditors[Constants.PropertyEditors.TextboxAlias]; - Current.Logger.Warn("Could not resolve a parameter editor with alias " + property.EditorAlias + ", a textbox will be rendered in it's place"); - } - - parameter.View = paramEditor.ValueEditor.View; - //set the config - parameter.Configuration = paramEditor.Configuration; - }); - } - } +using System.Collections.Generic; +using System.Linq; +using AutoMapper; +using Umbraco.Core; +using Umbraco.Core.Logging; +using Umbraco.Core.Models; +using Umbraco.Web.Composing; +using Umbraco.Web.Models.ContentEditing; + +namespace Umbraco.Web.Models.Mapping +{ + /// + /// Declares model mappings for macros. + /// + internal class MacroProfile : Profile + { + public MacroProfile() + { + //FROM IMacro TO EntityBasic + CreateMap() + .ForMember(x => x.Udi, expression => expression.MapFrom(content => Udi.Create(Constants.UdiEntityType.Macro, content.Key))) + .ForMember(entityBasic => entityBasic.Icon, expression => expression.UseValue("icon-settings-alt")) + .ForMember(dto => dto.ParentId, expression => expression.UseValue(-1)) + .ForMember(dto => dto.Path, expression => expression.ResolveUsing(macro => "-1," + macro.Id)) + .ForMember(dto => dto.Trashed, expression => expression.Ignore()) + .ForMember(dto => dto.AdditionalData, expression => expression.Ignore()); + + CreateMap>() + .ConvertUsing(macro => macro.Properties.Select(Mapper.Map).ToList()); + + CreateMap() + .ForMember(x => x.View, expression => expression.Ignore()) + .ForMember(x => x.Configuration, expression => expression.Ignore()) + .ForMember(x => x.Value, expression => expression.Ignore()) + .AfterMap((property, parameter) => + { + //map the view and the config + // we need to show the depracated ones for backwards compatibility + var paramEditor = Current.ParameterEditors[property.EditorAlias]; // fixme - include/filter deprecated?! + if (paramEditor == null) + { + //we'll just map this to a text box + paramEditor = Current.ParameterEditors[Constants.PropertyEditors.TextboxAlias]; + Current.Logger.Warn("Could not resolve a parameter editor with alias " + property.EditorAlias + ", a textbox will be rendered in it's place"); + } + + parameter.View = paramEditor.ValueEditor.View; + //set the config + parameter.Configuration = paramEditor.Configuration; + }); + } + } } \ No newline at end of file diff --git a/src/Umbraco.Web/Models/Mapping/MediaModelMapper.cs b/src/Umbraco.Web/Models/Mapping/MediaModelMapper.cs deleted file mode 100644 index e590521192..0000000000 --- a/src/Umbraco.Web/Models/Mapping/MediaModelMapper.cs +++ /dev/null @@ -1,154 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using System.Web; -using System.Web.Mvc; -using System.Web.Routing; -using AutoMapper; -using Umbraco.Core; -using Umbraco.Core.Configuration; -using Umbraco.Core.Logging; -using Umbraco.Core.Models; -using Umbraco.Core.Models.Mapping; -using Umbraco.Core.Services; -using Umbraco.Web.Composing; -using Umbraco.Web.Models.ContentEditing; -using Umbraco.Web.Trees; - -namespace Umbraco.Web.Models.Mapping -{ - /// - /// Declares model mappings for media. - /// - internal class MediaModelMapper : ModelMapperConfiguration - { - private readonly ILocalizedTextService _textService; - private readonly IUserService _userService; - private readonly IDataTypeService _dataTypeService; - private readonly IMediaService _mediaService; - private readonly ILogger _logger; - - public MediaModelMapper(IUserService userService, ILocalizedTextService textService, IDataTypeService dataTypeService, IMediaService mediaService, ILogger logger) - { - _userService = userService; - _textService = textService; - _dataTypeService = dataTypeService; - _mediaService = mediaService; - _logger = logger; - } - - public override void ConfigureMappings(IMapperConfiguration config) - { - //FROM IMedia TO MediaItemDisplay - config.CreateMap() - .ForMember(display => display.Udi, expression => expression.MapFrom(content => Udi.Create(Constants.UdiEntityType.Media, content.Key))) - .ForMember(display => display.Owner, expression => expression.ResolveUsing(new OwnerResolver(_userService))) - .ForMember(display => display.Icon, expression => expression.MapFrom(content => content.ContentType.Icon)) - .ForMember(display => display.ContentTypeAlias, expression => expression.MapFrom(content => content.ContentType.Alias)) - .ForMember(display => display.IsChildOfListView, expression => expression.Ignore()) - .ForMember(display => display.Trashed, expression => expression.MapFrom(content => content.Trashed)) - .ForMember(display => display.ContentTypeName, expression => expression.MapFrom(content => content.ContentType.Name)) - .ForMember(display => display.Properties, expression => expression.Ignore()) - .ForMember(display => display.TreeNodeUrl, expression => expression.Ignore()) - .ForMember(display => display.Notifications, expression => expression.Ignore()) - .ForMember(display => display.Errors, expression => expression.Ignore()) - .ForMember(display => display.Published, expression => expression.Ignore()) - .ForMember(display => display.Updater, expression => expression.Ignore()) - .ForMember(display => display.Alias, expression => expression.Ignore()) - .ForMember(display => display.IsContainer, expression => expression.Ignore()) - .ForMember(display => display.HasPublishedVersion, expression => expression.Ignore()) - .ForMember(display => display.Tabs, expression => expression.ResolveUsing(new TabsAndPropertiesResolver(_textService))) - .AfterMap((media, display) => AfterMap(media, display, _dataTypeService, _textService, _logger, _mediaService)); - - //FROM IMedia TO ContentItemBasic - config.CreateMap>() - .ForMember(display => display.Udi, expression => expression.MapFrom(content => Udi.Create(Constants.UdiEntityType.Media, content.Key))) - .ForMember(dto => dto.Owner, expression => expression.ResolveUsing(new OwnerResolver(_userService))) - .ForMember(dto => dto.Icon, expression => expression.MapFrom(content => content.ContentType.Icon)) - .ForMember(dto => dto.Trashed, expression => expression.MapFrom(content => content.Trashed)) - .ForMember(dto => dto.ContentTypeAlias, expression => expression.MapFrom(content => content.ContentType.Alias)) - .ForMember(dto => dto.Published, expression => expression.Ignore()) - .ForMember(dto => dto.Updater, expression => expression.Ignore()) - .ForMember(dto => dto.Alias, expression => expression.Ignore()) - .ForMember(dto => dto.HasPublishedVersion, expression => expression.Ignore()); - - //FROM IMedia TO ContentItemDto - config.CreateMap>() - .ForMember(display => display.Udi, expression => expression.MapFrom(content => Udi.Create(Constants.UdiEntityType.Media, content.Key))) - .ForMember(dto => dto.Owner, expression => expression.ResolveUsing(new OwnerResolver(_userService))) - .ForMember(dto => dto.Published, expression => expression.Ignore()) - .ForMember(dto => dto.Updater, expression => expression.Ignore()) - .ForMember(dto => dto.Icon, expression => expression.Ignore()) - .ForMember(dto => dto.Alias, expression => expression.Ignore()) - .ForMember(dto => dto.HasPublishedVersion, expression => expression.Ignore()); - } - - private static void AfterMap(IMedia media, MediaItemDisplay display, IDataTypeService dataTypeService, ILocalizedTextService localizedText, ILogger logger, IMediaService mediaService) - { - // Adapted from ContentModelMapper - //map the IsChildOfListView (this is actually if it is a descendant of a list view!) - var parent = media.Parent(); - display.IsChildOfListView = parent != null && (parent.ContentType.IsContainer || Current.Services.ContentTypeService.HasContainerInPath(parent.Path)); - - //map the tree node url - if (HttpContext.Current != null) - { - var urlHelper = new UrlHelper(HttpContext.Current.Request.RequestContext); - var url = urlHelper.GetUmbracoApiService(controller => controller.GetTreeNode(display.Id.ToString(), null)); - display.TreeNodeUrl = url; - } - - if (media.ContentType.IsContainer) - { - TabsAndPropertiesResolver.AddListView(display, "media", dataTypeService, localizedText); - } - - var genericProperties = new List - { - new ContentPropertyDisplay - { - Alias = string.Format("{0}doctype", Constants.PropertyEditors.InternalGenericPropertiesPrefix), - Label = localizedText.Localize("content/mediatype"), - Value = localizedText.UmbracoDictionaryTranslate(display.ContentTypeName), - View = Current.PropertyEditors[Constants.PropertyEditors.NoEditAlias].ValueEditor.View - } - }; - - TabsAndPropertiesResolver.MapGenericProperties(media, display, localizedText, genericProperties, properties => - { - if (HttpContext.Current != null && UmbracoContext.Current != null && UmbracoContext.Current.Security.CurrentUser != null - && UmbracoContext.Current.Security.CurrentUser.AllowedSections.Any(x => x.Equals(Constants.Applications.Settings))) - { - var mediaTypeLink = string.Format("#/settings/mediatypes/edit/{0}", media.ContentTypeId); - - //Replace the doctype property - var docTypeProperty = properties.First(x => x.Alias == string.Format("{0}doctype", Constants.PropertyEditors.InternalGenericPropertiesPrefix)); - docTypeProperty.Value = new List - { - new - { - linkText = media.ContentType.Name, - url = mediaTypeLink, - target = "_self", - icon = "icon-item-arrangement" - } - }; - docTypeProperty.View = "urllist"; - } - - // inject 'Link to media' as the first generic property - var links = media.GetUrls(UmbracoConfig.For.UmbracoSettings().Content, logger); - if (links.Any()) - { - var link = new ContentPropertyDisplay - { - Alias = string.Format("{0}urls", Constants.PropertyEditors.InternalGenericPropertiesPrefix), - Label = localizedText.Localize("media/urls"), - Value = string.Join(",", links), - View = "urllist" - }; - properties.Insert(0, link); - } - }); - } - } -} diff --git a/src/Umbraco.Web/Models/Mapping/MediaProfile.cs b/src/Umbraco.Web/Models/Mapping/MediaProfile.cs new file mode 100644 index 0000000000..482d0f33a4 --- /dev/null +++ b/src/Umbraco.Web/Models/Mapping/MediaProfile.cs @@ -0,0 +1,141 @@ +using System.Collections.Generic; +using System.Linq; +using System.Web; +using System.Web.Mvc; +using AutoMapper; +using Umbraco.Core; +using Umbraco.Core.Configuration; +using Umbraco.Core.Logging; +using Umbraco.Core.Models; +using Umbraco.Core.Services; +using Umbraco.Web.Composing; +using Umbraco.Web.Models.ContentEditing; +using Umbraco.Web.Trees; + +namespace Umbraco.Web.Models.Mapping +{ + /// + /// Declares model mappings for media. + /// + internal class MediaProfile : Profile + { + public MediaProfile(IUserService userService, ILocalizedTextService textService, IDataTypeService dataTypeService, IMediaService mediaService, ILogger logger) + { + // create, capture, cache + var mediaOwnerResolver = new OwnerResolver(userService); + var tabsAndPropertiesResolver = new TabsAndPropertiesResolver(textService); + + //FROM IMedia TO MediaItemDisplay + CreateMap() + .ForMember(dest => dest.Udi, opt => opt.MapFrom(content => Udi.Create(Constants.UdiEntityType.Media, content.Key))) + .ForMember(dest => dest.Owner, opt => opt.ResolveUsing(src => mediaOwnerResolver.Resolve(src))) + .ForMember(dest => dest.Icon, opt => opt.MapFrom(content => content.ContentType.Icon)) + .ForMember(dest => dest.ContentTypeAlias, opt => opt.MapFrom(content => content.ContentType.Alias)) + .ForMember(dest => dest.IsChildOfListView, opt => opt.Ignore()) + .ForMember(dest => dest.Trashed, opt => opt.MapFrom(content => content.Trashed)) + .ForMember(dest => dest.ContentTypeName, opt => opt.MapFrom(content => content.ContentType.Name)) + .ForMember(dest => dest.Properties, opt => opt.Ignore()) + .ForMember(dest => dest.TreeNodeUrl, opt => opt.Ignore()) + .ForMember(dest => dest.Notifications, opt => opt.Ignore()) + .ForMember(dest => dest.Errors, opt => opt.Ignore()) + .ForMember(dest => dest.Published, opt => opt.Ignore()) + .ForMember(dest => dest.Updater, opt => opt.Ignore()) + .ForMember(dest => dest.Alias, opt => opt.Ignore()) + .ForMember(dest => dest.IsContainer, opt => opt.Ignore()) + .ForMember(dest => dest.HasPublishedVersion, opt => opt.Ignore()) + .ForMember(dest => dest.Tabs, opt => opt.ResolveUsing(src => tabsAndPropertiesResolver.Resolve(src))) + .AfterMap((src, dest) => AfterMap(src, dest, dataTypeService, textService, logger, mediaService)); + + //FROM IMedia TO ContentItemBasic + CreateMap>() + .ForMember(dest => dest.Udi, opt => opt.MapFrom(src => Udi.Create(Constants.UdiEntityType.Media, src.Key))) + .ForMember(dest => dest.Owner, opt => opt.ResolveUsing(src => mediaOwnerResolver.Resolve(src))) + .ForMember(dest => dest.Icon, opt => opt.MapFrom(src => src.ContentType.Icon)) + .ForMember(dest => dest.Trashed, opt => opt.MapFrom(src => src.Trashed)) + .ForMember(dest => dest.ContentTypeAlias, opt => opt.MapFrom(src => src.ContentType.Alias)) + .ForMember(dest => dest.Published, opt => opt.Ignore()) + .ForMember(dest => dest.Updater, opt => opt.Ignore()) + .ForMember(dest => dest.Alias, opt => opt.Ignore()) + .ForMember(dest => dest.HasPublishedVersion, opt => opt.Ignore()); + + //FROM IMedia TO ContentItemDto + CreateMap>() + .ForMember(dest => dest.Udi, opt => opt.MapFrom(src => Udi.Create(Constants.UdiEntityType.Media, src.Key))) + .ForMember(dest => dest.Owner, opt => opt.ResolveUsing(src => mediaOwnerResolver.Resolve(src))) + .ForMember(dest => dest.Published, opt => opt.Ignore()) + .ForMember(dest => dest.Updater, opt => opt.Ignore()) + .ForMember(dest => dest.Icon, opt => opt.Ignore()) + .ForMember(dest => dest.Alias, opt => opt.Ignore()) + .ForMember(dest => dest.HasPublishedVersion, opt => opt.Ignore()); + } + + private static void AfterMap(IMedia media, MediaItemDisplay display, IDataTypeService dataTypeService, ILocalizedTextService localizedText, ILogger logger, IMediaService mediaService) + { + // Adapted from ContentModelMapper + //map the IsChildOfListView (this is actually if it is a descendant of a list view!) + var parent = media.Parent(); + display.IsChildOfListView = parent != null && (parent.ContentType.IsContainer || Current.Services.ContentTypeService.HasContainerInPath(parent.Path)); + + //map the tree node url + if (HttpContext.Current != null) + { + var urlHelper = new UrlHelper(HttpContext.Current.Request.RequestContext); + var url = urlHelper.GetUmbracoApiService(controller => controller.GetTreeNode(display.Id.ToString(), null)); + display.TreeNodeUrl = url; + } + + if (media.ContentType.IsContainer) + { + TabsAndPropertiesResolver.AddListView(display, "media", dataTypeService, localizedText); + } + + var genericProperties = new List + { + new ContentPropertyDisplay + { + Alias = string.Format("{0}doctype", Constants.PropertyEditors.InternalGenericPropertiesPrefix), + Label = localizedText.Localize("content/mediatype"), + Value = localizedText.UmbracoDictionaryTranslate(display.ContentTypeName), + View = Current.PropertyEditors[Constants.PropertyEditors.NoEditAlias].ValueEditor.View + } + }; + + TabsAndPropertiesResolver.MapGenericProperties(media, display, localizedText, genericProperties, properties => + { + if (HttpContext.Current != null && UmbracoContext.Current != null && UmbracoContext.Current.Security.CurrentUser != null + && UmbracoContext.Current.Security.CurrentUser.AllowedSections.Any(x => x.Equals(Constants.Applications.Settings))) + { + var mediaTypeLink = string.Format("#/settings/mediatypes/edit/{0}", media.ContentTypeId); + + //Replace the doctype property + var docTypeProperty = properties.First(x => x.Alias == string.Format("{0}doctype", Constants.PropertyEditors.InternalGenericPropertiesPrefix)); + docTypeProperty.Value = new List + { + new + { + linkText = media.ContentType.Name, + url = mediaTypeLink, + target = "_self", + icon = "icon-item-arrangement" + } + }; + docTypeProperty.View = "urllist"; + } + + // inject 'Link to media' as the first generic property + var links = media.GetUrls(UmbracoConfig.For.UmbracoSettings().Content, logger); + if (links.Any()) + { + var link = new ContentPropertyDisplay + { + Alias = string.Format("{0}urls", Constants.PropertyEditors.InternalGenericPropertiesPrefix), + Label = localizedText.Localize("media/urls"), + Value = string.Join(",", links), + View = "urllist" + }; + properties.Insert(0, link); + } + }); + } + } +} diff --git a/src/Umbraco.Web/Models/Mapping/MemberDtoPropertiesResolver.cs b/src/Umbraco.Web/Models/Mapping/MemberDtoPropertiesResolver.cs new file mode 100644 index 0000000000..3c3d6860b0 --- /dev/null +++ b/src/Umbraco.Web/Models/Mapping/MemberDtoPropertiesResolver.cs @@ -0,0 +1,30 @@ +using System.Collections.Generic; +using System.Linq; +using AutoMapper; +using Umbraco.Core; +using Umbraco.Core.Models; +using Umbraco.Web.Models.ContentEditing; + +namespace Umbraco.Web.Models.Mapping +{ + /// + /// This ensures that the custom membership provider properties are not mapped - these property values are controller by the membership provider + /// + /// + /// Because these properties don't exist on the form, if we don't remove them for this map we'll get validation errors when posting data + /// + internal class MemberDtoPropertiesResolver + { + public IEnumerable Resolve(IMember source) + { + var defaultProps = Constants.Conventions.Member.GetStandardPropertyTypeStubs(); + + //remove all membership properties, these values are set with the membership provider. + var exclude = defaultProps.Select(x => x.Value.Alias).ToArray(); + + return source.Properties + .Where(x => exclude.Contains(x.Alias) == false) + .Select(Mapper.Map); + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Web/Models/Mapping/MemberModelMapper.cs b/src/Umbraco.Web/Models/Mapping/MemberModelMapper.cs deleted file mode 100644 index 0de0212529..0000000000 --- a/src/Umbraco.Web/Models/Mapping/MemberModelMapper.cs +++ /dev/null @@ -1,470 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Web; -using System.Web.Mvc; -using System.Web.Routing; -using System.Web.Security; -using AutoMapper; -using Umbraco.Core; -using Umbraco.Core.Models; -using Umbraco.Core.Models.Mapping; -using Umbraco.Core.Models.Membership; -using Umbraco.Core.Services; -using Umbraco.Web.Models.ContentEditing; -using System.Linq; -using Umbraco.Core.Security; -using Umbraco.Web.Composing; -using Umbraco.Web.Trees; - -namespace Umbraco.Web.Models.Mapping -{ - /// - /// Declares model mappings for members. - /// - internal class MemberModelMapper : ModelMapperConfiguration - { - private readonly IUserService _userService; - private readonly ILocalizedTextService _textService; - private readonly IMemberService _memberService; - private readonly IMemberTypeService _memberTypeService; - - public MemberModelMapper(IUserService userService, ILocalizedTextService textService, IMemberTypeService memberTypeService, IMemberService memberService) - { - _userService = userService; - _textService = textService; - _memberTypeService = memberTypeService; - _memberService = memberService; - } - - public override void ConfigureMappings(IMapperConfiguration config) - { - //FROM MembershipUser TO MediaItemDisplay - used when using a non-umbraco membership provider - config.CreateMap() - .ConvertUsing(user => - { - var member = Mapper.Map(user); - return Mapper.Map(member); - }); - - //FROM MembershipUser TO IMember - used when using a non-umbraco membership provider - config.CreateMap() - .ConstructUsing(user => MemberService.CreateGenericMembershipProviderMember(user.UserName, user.Email, user.UserName, "")) - //we're giving this entity an ID of 0 - we cannot really map it but it needs an id so the system knows it's not a new entity - .ForMember(member => member.Id, expression => expression.MapFrom(user => int.MaxValue)) - .ForMember(member => member.Comments, expression => expression.MapFrom(user => user.Comment)) - .ForMember(member => member.CreateDate, expression => expression.MapFrom(user => user.CreationDate)) - .ForMember(member => member.UpdateDate, expression => expression.MapFrom(user => user.LastActivityDate)) - .ForMember(member => member.LastPasswordChangeDate, expression => expression.MapFrom(user => user.LastPasswordChangedDate)) - .ForMember(member => member.Key, expression => expression.MapFrom(user => user.ProviderUserKey.TryConvertTo().Result.ToString("N"))) - //This is a special case for password - we don't actually care what the password is but it either needs to be something or nothing - // so we'll set it to something if the member is actually created, otherwise nothing if it is a new member. - .ForMember(member => member.RawPasswordValue, expression => expression.MapFrom(user => user.CreationDate > DateTime.MinValue ? Guid.NewGuid().ToString("N") : "")) - .ForMember(member => member.Properties, expression => expression.Ignore()) - .ForMember(member => member.CreatorId, expression => expression.Ignore()) - .ForMember(member => member.Level, expression => expression.Ignore()) - .ForMember(member => member.Name, expression => expression.Ignore()) - .ForMember(member => member.ParentId, expression => expression.Ignore()) - .ForMember(member => member.Path, expression => expression.Ignore()) - .ForMember(member => member.SortOrder, expression => expression.Ignore()) - .ForMember(member => member.AdditionalData, expression => expression.Ignore()) - .ForMember(member => member.FailedPasswordAttempts, expression => expression.Ignore()) - .ForMember(member => member.DeletedDate, expression => expression.Ignore()) - //TODO: Support these eventually - .ForMember(member => member.PasswordQuestion, expression => expression.Ignore()) - .ForMember(member => member.RawPasswordAnswerValue, expression => expression.Ignore()); - - //FROM IMember TO MediaItemDisplay - config.CreateMap() - .ForMember(display => display.Udi, expression => expression.MapFrom(content => Udi.Create(Constants.UdiEntityType.Member, content.Key))) - .ForMember(display => display.Owner, expression => expression.ResolveUsing(new OwnerResolver(_userService))) - .ForMember(display => display.Icon, expression => expression.MapFrom(content => content.ContentType.Icon)) - .ForMember(display => display.ContentTypeAlias, expression => expression.MapFrom(content => content.ContentType.Alias)) - .ForMember(display => display.ContentTypeName, expression => expression.MapFrom(content => content.ContentType.Name)) - .ForMember(display => display.Properties, expression => expression.Ignore()) - .ForMember(display => display.Tabs, expression => expression.ResolveUsing(new MemberTabsAndPropertiesResolver(_textService))) - .ForMember(display => display.MemberProviderFieldMapping, expression => expression.ResolveUsing(new MemberProviderFieldMappingResolver())) - .ForMember(display => display.MembershipScenario, - expression => expression.ResolveUsing(new MembershipScenarioMappingResolver(new Lazy(() => _memberTypeService)))) - .ForMember(display => display.Notifications, expression => expression.Ignore()) - .ForMember(display => display.Errors, expression => expression.Ignore()) - .ForMember(display => display.Published, expression => expression.Ignore()) - .ForMember(display => display.Updater, expression => expression.Ignore()) - .ForMember(display => display.Alias, expression => expression.Ignore()) - .ForMember(display => display.IsChildOfListView, expression => expression.Ignore()) - .ForMember(display => display.Trashed, expression => expression.Ignore()) - .ForMember(display => display.IsContainer, expression => expression.Ignore()) - .ForMember(display => display.TreeNodeUrl, expression => expression.Ignore()) - .ForMember(display => display.HasPublishedVersion, expression => expression.Ignore()) - .AfterMap((member, display) => MapGenericCustomProperties(_memberService, _userService, member, display, _textService)); - - //FROM IMember TO MemberBasic - config.CreateMap() - .ForMember(display => display.Udi, expression => expression.MapFrom(content => Udi.Create(Constants.UdiEntityType.Member, content.Key))) - .ForMember(dto => dto.Owner, expression => expression.ResolveUsing(new OwnerResolver(_userService))) - .ForMember(dto => dto.Icon, expression => expression.MapFrom(content => content.ContentType.Icon)) - .ForMember(dto => dto.ContentTypeAlias, expression => expression.MapFrom(content => content.ContentType.Alias)) - .ForMember(dto => dto.Email, expression => expression.MapFrom(content => content.Email)) - .ForMember(dto => dto.Username, expression => expression.MapFrom(content => content.Username)) - .ForMember(dto => dto.Trashed, expression => expression.Ignore()) - .ForMember(dto => dto.Published, expression => expression.Ignore()) - .ForMember(dto => dto.Updater, expression => expression.Ignore()) - .ForMember(dto => dto.Alias, expression => expression.Ignore()) - .ForMember(dto => dto.HasPublishedVersion, expression => expression.Ignore()); - - //FROM MembershipUser TO MemberBasic - config.CreateMap() - //we're giving this entity an ID of 0 - we cannot really map it but it needs an id so the system knows it's not a new entity - .ForMember(member => member.Id, expression => expression.MapFrom(user => int.MaxValue)) - .ForMember(display => display.Udi, expression => expression.Ignore()) - .ForMember(member => member.CreateDate, expression => expression.MapFrom(user => user.CreationDate)) - .ForMember(member => member.UpdateDate, expression => expression.MapFrom(user => user.LastActivityDate)) - .ForMember(member => member.Key, expression => expression.MapFrom(user => user.ProviderUserKey.TryConvertTo().Result.ToString("N"))) - .ForMember(member => member.Owner, expression => expression.UseValue(new UserBasic {Name = "Admin", UserId = 0})) - .ForMember(member => member.Icon, expression => expression.UseValue("icon-user")) - .ForMember(member => member.Name, expression => expression.MapFrom(user => user.UserName)) - .ForMember(member => member.Email, expression => expression.MapFrom(content => content.Email)) - .ForMember(member => member.Username, expression => expression.MapFrom(content => content.UserName)) - .ForMember(member => member.Properties, expression => expression.Ignore()) - .ForMember(member => member.ParentId, expression => expression.Ignore()) - .ForMember(member => member.Path, expression => expression.Ignore()) - .ForMember(member => member.SortOrder, expression => expression.Ignore()) - .ForMember(member => member.AdditionalData, expression => expression.Ignore()) - .ForMember(member => member.Published, expression => expression.Ignore()) - .ForMember(member => member.Updater, expression => expression.Ignore()) - .ForMember(member => member.Trashed, expression => expression.Ignore()) - .ForMember(member => member.Alias, expression => expression.Ignore()) - .ForMember(member => member.ContentTypeAlias, expression => expression.Ignore()) - .ForMember(member => member.HasPublishedVersion, expression => expression.Ignore()); - - //FROM IMember TO ContentItemDto - config.CreateMap>() - .ForMember(display => display.Udi, expression => expression.MapFrom(content => Udi.Create(Constants.UdiEntityType.Member, content.Key))) - .ForMember(dto => dto.Owner, expression => expression.ResolveUsing(new OwnerResolver(_userService))) - .ForMember(dto => dto.Published, expression => expression.Ignore()) - .ForMember(dto => dto.Updater, expression => expression.Ignore()) - .ForMember(dto => dto.Icon, expression => expression.Ignore()) - .ForMember(dto => dto.Alias, expression => expression.Ignore()) - .ForMember(dto => dto.HasPublishedVersion, expression => expression.Ignore()) - //do no map the custom member properties (currently anyways, they were never there in 6.x) - .ForMember(dto => dto.Properties, expression => expression.ResolveUsing(new MemberDtoPropertiesValueResolver())); - - //FROM IMemberGroup TO MemberGroupDisplay - config.CreateMap() - .ForMember(dst => dst.Udi, expression => expression.MapFrom(src => Udi.Create(Constants.UdiEntityType.MemberGroup, src.Key))) - .ForMember(x => x.Path, expression => expression.MapFrom(group => "-1," + group.Id)) - .ForMember(x => x.Notifications, expression => expression.Ignore()) - .ForMember(x => x.Icon, expression => expression.Ignore()) - .ForMember(x => x.Trashed, expression => expression.Ignore()) - .ForMember(x => x.ParentId, expression => expression.Ignore()) - .ForMember(x => x.Alias, expression => expression.Ignore()) - .ForMember(x => x.Path, expression => expression.Ignore()); - } - - /// - /// Maps the generic tab with custom properties for content - /// - /// - /// - /// - /// - /// - /// - /// If this is a new entity and there is an approved field then we'll set it to true by default. - /// - private static void MapGenericCustomProperties(IMemberService memberService, IUserService userService, IMember member, MemberDisplay display, ILocalizedTextService localizedText) - { - var membersProvider = Core.Security.MembershipProviderExtensions.GetMembersMembershipProvider(); - - //map the tree node url - if (HttpContext.Current != null) - { - var urlHelper = new UrlHelper(HttpContext.Current.Request.RequestContext); - var url = urlHelper.GetUmbracoApiService(controller => controller.GetTreeNode(display.Key.ToString("N"), null)); - display.TreeNodeUrl = url; - } - - var genericProperties = new List - { - new ContentPropertyDisplay - { - Alias = string.Format("{0}doctype", Constants.PropertyEditors.InternalGenericPropertiesPrefix), - Label = localizedText.Localize("content/membertype"), - Value = localizedText.UmbracoDictionaryTranslate(display.ContentTypeName), - View = Current.PropertyEditors[Constants.PropertyEditors.NoEditAlias].ValueEditor.View - }, - GetLoginProperty(memberService, member, display, localizedText), - new ContentPropertyDisplay - { - Alias = string.Format("{0}email", Constants.PropertyEditors.InternalGenericPropertiesPrefix), - Label = localizedText.Localize("general/email"), - Value = display.Email, - View = "email", - Validation = {Mandatory = true} - }, - new ContentPropertyDisplay - { - Alias = string.Format("{0}password", Constants.PropertyEditors.InternalGenericPropertiesPrefix), - Label = localizedText.Localize("password"), - //NOTE: The value here is a json value - but the only property we care about is the generatedPassword one if it exists, the newPassword exists - // only when creating a new member and we want to have a generated password pre-filled. - Value = new Dictionary - { - {"generatedPassword", member.GetAdditionalDataValueIgnoreCase("GeneratedPassword", null)}, - {"newPassword", member.GetAdditionalDataValueIgnoreCase("NewPassword", null)}, - }, - //TODO: Hard coding this because the changepassword doesn't necessarily need to be a resolvable (real) property editor - View = "changepassword", - //initialize the dictionary with the configuration from the default membership provider - Config = new Dictionary(membersProvider.GetConfiguration(userService)) - { - //the password change toggle will only be displayed if there is already a password assigned. - {"hasPassword", member.RawPasswordValue.IsNullOrWhiteSpace() == false} - } - }, - new ContentPropertyDisplay - { - Alias = string.Format("{0}membergroup", Constants.PropertyEditors.InternalGenericPropertiesPrefix), - Label = localizedText.Localize("content/membergroup"), - Value = GetMemberGroupValue(display.Username), - View = "membergroups", - Config = new Dictionary {{"IsRequired", true}} - } - }; - - TabsAndPropertiesResolver.MapGenericProperties(member, display, localizedText, genericProperties, properties => - { - if (HttpContext.Current != null && UmbracoContext.Current != null && UmbracoContext.Current.Security.CurrentUser != null - && UmbracoContext.Current.Security.CurrentUser.AllowedSections.Any(x => x.Equals(Constants.Applications.Settings))) - { - var memberTypeLink = string.Format("#/member/memberTypes/edit/{0}", member.ContentTypeId); - - //Replace the doctype property - var docTypeProperty = properties.First(x => x.Alias == string.Format("{0}doctype", Constants.PropertyEditors.InternalGenericPropertiesPrefix)); - docTypeProperty.Value = new List - { - new - { - linkText = member.ContentType.Name, - url = memberTypeLink, - target = "_self", - icon = "icon-item-arrangement" - } - }; - docTypeProperty.View = "urllist"; - } - }); - - //check if there's an approval field - var provider = membersProvider as IUmbracoMemberTypeMembershipProvider; - if (member.HasIdentity == false && provider != null) - { - var approvedField = provider.ApprovedPropertyTypeAlias; - var prop = display.Properties.FirstOrDefault(x => x.Alias == approvedField); - if (prop != null) - { - prop.Value = 1; - } - } - } - - /// - /// Returns the login property display field - /// - /// - /// - /// - /// - /// - /// - /// If the membership provider installed is the umbraco membership provider, then we will allow changing the username, however if - /// the membership provider is a custom one, we cannot allow chaning the username because MembershipProvider's do not actually natively - /// allow that. - /// - internal static ContentPropertyDisplay GetLoginProperty(IMemberService memberService, IMember member, MemberDisplay display, ILocalizedTextService localizedText) - { - var prop = new ContentPropertyDisplay - { - Alias = string.Format("{0}login", Constants.PropertyEditors.InternalGenericPropertiesPrefix), - Label = localizedText.Localize("login"), - Value = display.Username - }; - - var scenario = memberService.GetMembershipScenario(); - - //only allow editing if this is a new member, or if the membership provider is the umbraco one - if (member.HasIdentity == false || scenario == MembershipScenario.NativeUmbraco) - { - prop.View = "textbox"; - prop.Validation.Mandatory = true; - } - else - { - prop.View = "readonlyvalue"; - } - return prop; - } - - internal static IDictionary GetMemberGroupValue(string username) - { - var userRoles = username.IsNullOrWhiteSpace() ? null : Roles.GetRolesForUser(username); - - // create a dictionary of all roles (except internal roles) + "false" - var result = Roles.GetAllRoles().Distinct() - // if a role starts with __umbracoRole we won't show it as it's an internal role used for public access - .Where(x => x.StartsWith(Constants.Conventions.Member.InternalRolePrefix) == false) - .ToDictionary(x => x, x => false); - - // if user has no roles, just return the dictionary - 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; - } - - /// - /// This ensures that the custom membership provider properties are not mapped - these property values are controller by the membership provider - /// - /// - /// Because these properties don't exist on the form, if we don't remove them for this map we'll get validation errors when posting data - /// - internal class MemberDtoPropertiesValueResolver : ValueResolver> - { - protected override IEnumerable ResolveCore(IMember source) - { - var defaultProps = Constants.Conventions.Member.GetStandardPropertyTypeStubs(); - - //remove all membership properties, these values are set with the membership provider. - var exclude = defaultProps.Select(x => x.Value.Alias).ToArray(); - - return source.Properties - .Where(x => exclude.Contains(x.Alias) == false) - .Select(Mapper.Map); - } - } - - /// - /// A custom tab/property resolver for members which will ensure that the built-in membership properties are or arent' displayed - /// depending on if the member type has these properties - /// - /// - /// This also ensures that the IsLocked out property is readonly when the member is not locked out - this is because - /// an admin cannot actually set isLockedOut = true, they can only unlock. - /// - internal class MemberTabsAndPropertiesResolver : TabsAndPropertiesResolver - { - private readonly ILocalizedTextService _localizedTextService; - - public MemberTabsAndPropertiesResolver(ILocalizedTextService localizedTextService) - : base(localizedTextService) - { - _localizedTextService = localizedTextService; - } - - public MemberTabsAndPropertiesResolver(ILocalizedTextService localizedTextService, - IEnumerable ignoreProperties) : base(localizedTextService, ignoreProperties) - { - _localizedTextService = localizedTextService; - } - - protected override IEnumerable> ResolveCore(IContentBase content) - { - var provider = Core.Security.MembershipProviderExtensions.GetMembersMembershipProvider(); - - IgnoreProperties = content.PropertyTypes - .Where(x => x.HasIdentity == false) - .Select(x => x.Alias) - .ToArray(); - - var result = base.ResolveCore(content).ToArray(); - - if (provider.IsUmbracoMembershipProvider() == false) - { - //it's a generic provider so update the locked out property based on our known constant alias - var isLockedOutProperty = result.SelectMany(x => x.Properties).FirstOrDefault(x => x.Alias == Constants.Conventions.Member.IsLockedOut); - if (isLockedOutProperty != null && isLockedOutProperty.Value.ToString() != "1") - { - isLockedOutProperty.View = "readonlyvalue"; - isLockedOutProperty.Value = _localizedTextService.Localize("general/no"); - } - - return result; - } - else - { - var umbracoProvider = (IUmbracoMemberTypeMembershipProvider) provider; - - //This is kind of a hack because a developer is supposed to be allowed to set their property editor - would have been much easier - // if we just had all of the membeship provider fields on the member table :( - // TODO: But is there a way to map the IMember.IsLockedOut to the property ? i dunno. - var isLockedOutProperty = result.SelectMany(x => x.Properties).FirstOrDefault(x => x.Alias == umbracoProvider.LockPropertyTypeAlias); - if (isLockedOutProperty != null && isLockedOutProperty.Value.ToString() != "1") - { - isLockedOutProperty.View = "readonlyvalue"; - isLockedOutProperty.Value = _localizedTextService.Localize("general/no"); - } - - return result; - } - } - } - - internal class MembershipScenarioMappingResolver : ValueResolver - { - private readonly Lazy _memberTypeService; - - public MembershipScenarioMappingResolver(Lazy memberTypeService) - { - _memberTypeService = memberTypeService; - } - - protected override MembershipScenario ResolveCore(IMember source) - { - var provider = Core.Security.MembershipProviderExtensions.GetMembersMembershipProvider(); - - if (provider.IsUmbracoMembershipProvider()) - { - return MembershipScenario.NativeUmbraco; - } - var memberType = _memberTypeService.Value.Get(Constants.Conventions.MemberTypes.DefaultAlias); - return memberType != null - ? MembershipScenario.CustomProviderWithUmbracoLink - : MembershipScenario.StandaloneCustomProvider; - } - } - - /// - /// A resolver to map the provider field aliases - /// - internal class MemberProviderFieldMappingResolver : ValueResolver> - { - protected override IDictionary ResolveCore(IMember source) - { - var provider = Core.Security.MembershipProviderExtensions.GetMembersMembershipProvider(); - - if (provider.IsUmbracoMembershipProvider() == false) - { - return new Dictionary - { - {Constants.Conventions.Member.IsLockedOut, Constants.Conventions.Member.IsLockedOut}, - {Constants.Conventions.Member.IsApproved, Constants.Conventions.Member.IsApproved}, - {Constants.Conventions.Member.Comments, Constants.Conventions.Member.Comments} - }; - } - else - { - var umbracoProvider = (IUmbracoMemberTypeMembershipProvider) provider; - - return new Dictionary - { - {Constants.Conventions.Member.IsLockedOut, umbracoProvider.LockPropertyTypeAlias}, - {Constants.Conventions.Member.IsApproved, umbracoProvider.ApprovedPropertyTypeAlias}, - {Constants.Conventions.Member.Comments, umbracoProvider.CommentPropertyTypeAlias} - }; - } - } - } - } -} \ No newline at end of file diff --git a/src/Umbraco.Web/Models/Mapping/MemberProfile.cs b/src/Umbraco.Web/Models/Mapping/MemberProfile.cs new file mode 100644 index 0000000000..f9093d4bc9 --- /dev/null +++ b/src/Umbraco.Web/Models/Mapping/MemberProfile.cs @@ -0,0 +1,318 @@ +using System; +using System.Collections.Generic; +using System.Web; +using System.Web.Mvc; +using System.Web.Security; +using AutoMapper; +using Umbraco.Core; +using Umbraco.Core.Models; +using Umbraco.Core.Models.Membership; +using Umbraco.Core.Services; +using Umbraco.Web.Models.ContentEditing; +using System.Linq; +using Umbraco.Core.Security; +using Umbraco.Web.Composing; +using Umbraco.Web.Trees; + +namespace Umbraco.Web.Models.Mapping +{ + /// + /// Declares model mappings for members. + /// + internal class MemberProfile : Profile + { + public MemberProfile(IUserService userService, ILocalizedTextService textService, IMemberTypeService memberTypeService, IMemberService memberService) + { + // create, capture, cache + var memberOwnerResolver = new OwnerResolver(userService); + var tabsAndPropertiesResolver = new MemberTabsAndPropertiesResolver(textService); + var memberProfiderFieldMappingResolver = new MemberProviderFieldResolver(); + var membershipScenarioMappingResolver = new MembershipScenarioResolver(new Lazy(() => memberTypeService)); + var memberDtoPropertiesResolver = new MemberDtoPropertiesResolver(); + + //FROM MembershipUser TO MediaItemDisplay - used when using a non-umbraco membership provider + CreateMap() + .ConvertUsing(user => + { + var member = Mapper.Map(user); + return Mapper.Map(member); + }); + + //FROM MembershipUser TO IMember - used when using a non-umbraco membership provider + CreateMap() + .ConstructUsing(src => MemberService.CreateGenericMembershipProviderMember(src.UserName, src.Email, src.UserName, "")) + //we're giving this entity an ID of 0 - we cannot really map it but it needs an id so the system knows it's not a new entity + .ForMember(dest => dest.Id, opt => opt.MapFrom(src => int.MaxValue)) + .ForMember(dest => dest.Comments, opt => opt.MapFrom(src => src.Comment)) + .ForMember(dest => dest.CreateDate, opt => opt.MapFrom(src => src.CreationDate)) + .ForMember(dest => dest.UpdateDate, opt => opt.MapFrom(src => src.LastActivityDate)) + .ForMember(dest => dest.LastPasswordChangeDate, opt => opt.MapFrom(src => src.LastPasswordChangedDate)) + .ForMember(dest => dest.Key, opt => opt.MapFrom(src => src.ProviderUserKey.TryConvertTo().Result.ToString("N"))) + //This is a special case for password - we don't actually care what the password is but it either needs to be something or nothing + // so we'll set it to something if the member is actually created, otherwise nothing if it is a new member. + .ForMember(dest => dest.RawPasswordValue, opt => opt.MapFrom(src => src.CreationDate > DateTime.MinValue ? Guid.NewGuid().ToString("N") : "")) + .ForMember(dest => dest.Properties, opt => opt.Ignore()) + .ForMember(dest => dest.CreatorId, opt => opt.Ignore()) + .ForMember(dest => dest.Level, opt => opt.Ignore()) + .ForMember(dest => dest.Name, opt => opt.Ignore()) + .ForMember(dest => dest.ParentId, opt => opt.Ignore()) + .ForMember(dest => dest.Path, opt => opt.Ignore()) + .ForMember(dest => dest.SortOrder, opt => opt.Ignore()) + .ForMember(dest => dest.AdditionalData, opt => opt.Ignore()) + .ForMember(dest => dest.FailedPasswordAttempts, opt => opt.Ignore()) + .ForMember(dest => dest.DeletedDate, opt => opt.Ignore()) + //TODO: Support these eventually + .ForMember(dest => dest.PasswordQuestion, opt => opt.Ignore()) + .ForMember(dest => dest.RawPasswordAnswerValue, opt => opt.Ignore()); + + //FROM IMember TO MediaItemDisplay + CreateMap() + .ForMember(dest => dest.Udi, opt => opt.MapFrom(content => Udi.Create(Constants.UdiEntityType.Member, content.Key))) + .ForMember(dest => dest.Owner, opt => opt.ResolveUsing(src => memberOwnerResolver.Resolve(src))) + .ForMember(dest => dest.Icon, opt => opt.MapFrom(src => src.ContentType.Icon)) + .ForMember(dest => dest.ContentTypeAlias, opt => opt.MapFrom(src => src.ContentType.Alias)) + .ForMember(dest => dest.ContentTypeName, opt => opt.MapFrom(src => src.ContentType.Name)) + .ForMember(dest => dest.Properties, opt => opt.Ignore()) + .ForMember(dest => dest.Tabs, opt => opt.ResolveUsing(src => tabsAndPropertiesResolver.Resolve(src))) + .ForMember(dest => dest.MemberProviderFieldMapping, opt => opt.ResolveUsing(src => memberProfiderFieldMappingResolver.Resolve(src))) + .ForMember(dest => dest.MembershipScenario, opt => opt.ResolveUsing(src => membershipScenarioMappingResolver.Resolve(src))) + .ForMember(dest => dest.Notifications, opt => opt.Ignore()) + .ForMember(dest => dest.Errors, opt => opt.Ignore()) + .ForMember(dest => dest.Published, opt => opt.Ignore()) + .ForMember(dest => dest.Updater, opt => opt.Ignore()) + .ForMember(dest => dest.Alias, opt => opt.Ignore()) + .ForMember(dest => dest.IsChildOfListView, opt => opt.Ignore()) + .ForMember(dest => dest.Trashed, opt => opt.Ignore()) + .ForMember(dest => dest.IsContainer, opt => opt.Ignore()) + .ForMember(dest => dest.TreeNodeUrl, opt => opt.Ignore()) + .ForMember(dest => dest.HasPublishedVersion, opt => opt.Ignore()) + .AfterMap((src, dest) => MapGenericCustomProperties(memberService, userService, src, dest, textService)); + + //FROM IMember TO MemberBasic + CreateMap() + .ForMember(dest => dest.Udi, opt => opt.MapFrom(content => Udi.Create(Constants.UdiEntityType.Member, content.Key))) + .ForMember(dest => dest.Owner, opt => opt.ResolveUsing(src => memberOwnerResolver.Resolve(src))) + .ForMember(dest => dest.Icon, opt => opt.MapFrom(src => src.ContentType.Icon)) + .ForMember(dest => dest.ContentTypeAlias, opt => opt.MapFrom(src => src.ContentType.Alias)) + .ForMember(dest => dest.Email, opt => opt.MapFrom(src => src.Email)) + .ForMember(dest => dest.Username, opt => opt.MapFrom(src => src.Username)) + .ForMember(dest => dest.Trashed, opt => opt.Ignore()) + .ForMember(dest => dest.Published, opt => opt.Ignore()) + .ForMember(dest => dest.Updater, opt => opt.Ignore()) + .ForMember(dest => dest.Alias, opt => opt.Ignore()) + .ForMember(dest => dest.HasPublishedVersion, opt => opt.Ignore()); + + //FROM MembershipUser TO MemberBasic + CreateMap() + //we're giving this entity an ID of 0 - we cannot really map it but it needs an id so the system knows it's not a new entity + .ForMember(dest => dest.Id, opt => opt.MapFrom(src => int.MaxValue)) + .ForMember(dest => dest.Udi, opt => opt.Ignore()) + .ForMember(dest => dest.CreateDate, opt => opt.MapFrom(src => src.CreationDate)) + .ForMember(dest => dest.UpdateDate, opt => opt.MapFrom(src => src.LastActivityDate)) + .ForMember(dest => dest.Key, opt => opt.MapFrom(src => src.ProviderUserKey.TryConvertTo().Result.ToString("N"))) + .ForMember(dest => dest.Owner, opt => opt.UseValue(new UserBasic {Name = "Admin", UserId = 0})) + .ForMember(dest => dest.Icon, opt => opt.UseValue("icon-user")) + .ForMember(dest => dest.Name, opt => opt.MapFrom(src => src.UserName)) + .ForMember(dest => dest.Email, opt => opt.MapFrom(src => src.Email)) + .ForMember(dest => dest.Username, opt => opt.MapFrom(src => src.UserName)) + .ForMember(dest => dest.Properties, opt => opt.Ignore()) + .ForMember(dest => dest.ParentId, opt => opt.Ignore()) + .ForMember(dest => dest.Path, opt => opt.Ignore()) + .ForMember(dest => dest.SortOrder, opt => opt.Ignore()) + .ForMember(dest => dest.AdditionalData, opt => opt.Ignore()) + .ForMember(dest => dest.Published, opt => opt.Ignore()) + .ForMember(dest => dest.Updater, opt => opt.Ignore()) + .ForMember(dest => dest.Trashed, opt => opt.Ignore()) + .ForMember(dest => dest.Alias, opt => opt.Ignore()) + .ForMember(dest => dest.ContentTypeAlias, opt => opt.Ignore()) + .ForMember(dest => dest.HasPublishedVersion, opt => opt.Ignore()); + + //FROM IMember TO ContentItemDto + CreateMap>() + .ForMember(dest => dest.Udi, opt => opt.MapFrom(content => Udi.Create(Constants.UdiEntityType.Member, content.Key))) + .ForMember(dest => dest.Owner, opt => opt.ResolveUsing(src => memberOwnerResolver.Resolve(src))) + .ForMember(dest => dest.Published, opt => opt.Ignore()) + .ForMember(dest => dest.Updater, opt => opt.Ignore()) + .ForMember(dest => dest.Icon, opt => opt.Ignore()) + .ForMember(dest => dest.Alias, opt => opt.Ignore()) + .ForMember(dest => dest.HasPublishedVersion, opt => opt.Ignore()) + //do no map the custom member properties (currently anyways, they were never there in 6.x) + .ForMember(dest => dest.Properties, opt => opt.ResolveUsing(src => memberDtoPropertiesResolver.Resolve(src))); + + //FROM IMemberGroup TO MemberGroupDisplay + CreateMap() + .ForMember(dest => dest.Udi, opt => opt.MapFrom(src => Udi.Create(Constants.UdiEntityType.MemberGroup, src.Key))) + .ForMember(dest => dest.Path, opt => opt.MapFrom(group => "-1," + group.Id)) + .ForMember(dest => dest.Notifications, opt => opt.Ignore()) + .ForMember(dest => dest.Icon, opt => opt.Ignore()) + .ForMember(dest => dest.Trashed, opt => opt.Ignore()) + .ForMember(dest => dest.ParentId, opt => opt.Ignore()) + .ForMember(dest => dest.Alias, opt => opt.Ignore()) + .ForMember(dest => dest.Path, opt => opt.Ignore()); + } + + /// + /// Maps the generic tab with custom properties for content + /// + /// + /// + /// + /// + /// + /// + /// If this is a new entity and there is an approved field then we'll set it to true by default. + /// + private static void MapGenericCustomProperties(IMemberService memberService, IUserService userService, IMember member, MemberDisplay display, ILocalizedTextService localizedText) + { + var membersProvider = Core.Security.MembershipProviderExtensions.GetMembersMembershipProvider(); + + //map the tree node url + if (HttpContext.Current != null) + { + var urlHelper = new UrlHelper(HttpContext.Current.Request.RequestContext); + var url = urlHelper.GetUmbracoApiService(controller => controller.GetTreeNode(display.Key.ToString("N"), null)); + display.TreeNodeUrl = url; + } + + var genericProperties = new List + { + new ContentPropertyDisplay + { + Alias = string.Format("{0}doctype", Constants.PropertyEditors.InternalGenericPropertiesPrefix), + Label = localizedText.Localize("content/membertype"), + Value = localizedText.UmbracoDictionaryTranslate(display.ContentTypeName), + View = Current.PropertyEditors[Constants.PropertyEditors.NoEditAlias].ValueEditor.View + }, + GetLoginProperty(memberService, member, display, localizedText), + new ContentPropertyDisplay + { + Alias = string.Format("{0}email", Constants.PropertyEditors.InternalGenericPropertiesPrefix), + Label = localizedText.Localize("general/email"), + Value = display.Email, + View = "email", + Validation = {Mandatory = true} + }, + new ContentPropertyDisplay + { + Alias = string.Format("{0}password", Constants.PropertyEditors.InternalGenericPropertiesPrefix), + Label = localizedText.Localize("password"), + //NOTE: The value here is a json value - but the only property we care about is the generatedPassword one if it exists, the newPassword exists + // only when creating a new member and we want to have a generated password pre-filled. + Value = new Dictionary + { + {"generatedPassword", member.GetAdditionalDataValueIgnoreCase("GeneratedPassword", null)}, + {"newPassword", member.GetAdditionalDataValueIgnoreCase("NewPassword", null)}, + }, + //TODO: Hard coding this because the changepassword doesn't necessarily need to be a resolvable (real) property editor + View = "changepassword", + //initialize the dictionary with the configuration from the default membership provider + Config = new Dictionary(membersProvider.GetConfiguration(userService)) + { + //the password change toggle will only be displayed if there is already a password assigned. + {"hasPassword", member.RawPasswordValue.IsNullOrWhiteSpace() == false} + } + }, + new ContentPropertyDisplay + { + Alias = string.Format("{0}membergroup", Constants.PropertyEditors.InternalGenericPropertiesPrefix), + Label = localizedText.Localize("content/membergroup"), + Value = GetMemberGroupValue(display.Username), + View = "membergroups", + Config = new Dictionary {{"IsRequired", true}} + } + }; + + TabsAndPropertiesResolver.MapGenericProperties(member, display, localizedText, genericProperties, properties => + { + if (HttpContext.Current != null && UmbracoContext.Current != null && UmbracoContext.Current.Security.CurrentUser != null + && UmbracoContext.Current.Security.CurrentUser.AllowedSections.Any(x => x.Equals(Constants.Applications.Settings))) + { + var memberTypeLink = string.Format("#/member/memberTypes/edit/{0}", member.ContentTypeId); + + //Replace the doctype property + var docTypeProperty = properties.First(x => x.Alias == string.Format("{0}doctype", Constants.PropertyEditors.InternalGenericPropertiesPrefix)); + docTypeProperty.Value = new List + { + new + { + linkText = member.ContentType.Name, + url = memberTypeLink, + target = "_self", + icon = "icon-item-arrangement" + } + }; + docTypeProperty.View = "urllist"; + } + }); + + //check if there's an approval field + var provider = membersProvider as IUmbracoMemberTypeMembershipProvider; + if (member.HasIdentity == false && provider != null) + { + var approvedField = provider.ApprovedPropertyTypeAlias; + var prop = display.Properties.FirstOrDefault(x => x.Alias == approvedField); + if (prop != null) + { + prop.Value = 1; + } + } + } + + /// + /// Returns the login property display field + /// + /// + /// + /// + /// + /// + /// + /// If the membership provider installed is the umbraco membership provider, then we will allow changing the username, however if + /// the membership provider is a custom one, we cannot allow chaning the username because MembershipProvider's do not actually natively + /// allow that. + /// + internal static ContentPropertyDisplay GetLoginProperty(IMemberService memberService, IMember member, MemberDisplay display, ILocalizedTextService localizedText) + { + var prop = new ContentPropertyDisplay + { + Alias = string.Format("{0}login", Constants.PropertyEditors.InternalGenericPropertiesPrefix), + Label = localizedText.Localize("login"), + Value = display.Username + }; + + var scenario = memberService.GetMembershipScenario(); + + //only allow editing if this is a new member, or if the membership provider is the umbraco one + if (member.HasIdentity == false || scenario == MembershipScenario.NativeUmbraco) + { + prop.View = "textbox"; + prop.Validation.Mandatory = true; + } + else + { + prop.View = "readonlyvalue"; + } + return prop; + } + + internal static IDictionary GetMemberGroupValue(string username) + { + var userRoles = username.IsNullOrWhiteSpace() ? null : Roles.GetRolesForUser(username); + + // create a dictionary of all roles (except internal roles) + "false" + var result = Roles.GetAllRoles().Distinct() + // if a role starts with __umbracoRole we won't show it as it's an internal role used for public access + .Where(x => x.StartsWith(Constants.Conventions.Member.InternalRolePrefix) == false) + .ToDictionary(x => x, x => false); + + // if user has no roles, just return the dictionary + 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; + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Web/Models/Mapping/MemberProviderFieldResolver.cs b/src/Umbraco.Web/Models/Mapping/MemberProviderFieldResolver.cs new file mode 100644 index 0000000000..587b29c6d8 --- /dev/null +++ b/src/Umbraco.Web/Models/Mapping/MemberProviderFieldResolver.cs @@ -0,0 +1,39 @@ +using System.Collections.Generic; +using Umbraco.Core; +using Umbraco.Core.Models; +using Umbraco.Core.Security; + +namespace Umbraco.Web.Models.Mapping +{ + /// + /// A resolver to map the provider field aliases + /// + internal class MemberProviderFieldResolver + { + public IDictionary Resolve(IMember source) + { + var provider = Core.Security.MembershipProviderExtensions.GetMembersMembershipProvider(); + + if (provider.IsUmbracoMembershipProvider() == false) + { + return new Dictionary + { + {Constants.Conventions.Member.IsLockedOut, Constants.Conventions.Member.IsLockedOut}, + {Constants.Conventions.Member.IsApproved, Constants.Conventions.Member.IsApproved}, + {Constants.Conventions.Member.Comments, Constants.Conventions.Member.Comments} + }; + } + else + { + var umbracoProvider = (IUmbracoMemberTypeMembershipProvider) provider; + + return new Dictionary + { + {Constants.Conventions.Member.IsLockedOut, umbracoProvider.LockPropertyTypeAlias}, + {Constants.Conventions.Member.IsApproved, umbracoProvider.ApprovedPropertyTypeAlias}, + {Constants.Conventions.Member.Comments, umbracoProvider.CommentPropertyTypeAlias} + }; + } + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Web/Models/Mapping/MemberTabsAndPropertiesResolver.cs b/src/Umbraco.Web/Models/Mapping/MemberTabsAndPropertiesResolver.cs new file mode 100644 index 0000000000..222ac46154 --- /dev/null +++ b/src/Umbraco.Web/Models/Mapping/MemberTabsAndPropertiesResolver.cs @@ -0,0 +1,76 @@ +using System.Collections.Generic; +using System.Linq; +using Umbraco.Core; +using Umbraco.Core.Models; +using Umbraco.Core.Security; +using Umbraco.Core.Services; +using Umbraco.Web.Models.ContentEditing; + +namespace Umbraco.Web.Models.Mapping +{ + /// + /// A custom tab/property resolver for members which will ensure that the built-in membership properties are or arent' displayed + /// depending on if the member type has these properties + /// + /// + /// This also ensures that the IsLocked out property is readonly when the member is not locked out - this is because + /// an admin cannot actually set isLockedOut = true, they can only unlock. + /// + internal class MemberTabsAndPropertiesResolver : TabsAndPropertiesResolver + { + private readonly ILocalizedTextService _localizedTextService; + + public MemberTabsAndPropertiesResolver(ILocalizedTextService localizedTextService) + : base(localizedTextService) + { + _localizedTextService = localizedTextService; + } + + public MemberTabsAndPropertiesResolver(ILocalizedTextService localizedTextService, + IEnumerable ignoreProperties) : base(localizedTextService, ignoreProperties) + { + _localizedTextService = localizedTextService; + } + + public override IEnumerable> Resolve(IContentBase content) + { + var provider = Core.Security.MembershipProviderExtensions.GetMembersMembershipProvider(); + + IgnoreProperties = content.PropertyTypes + .Where(x => x.HasIdentity == false) + .Select(x => x.Alias) + .ToArray(); + + var result = base.Resolve(content).ToArray(); + + if (provider.IsUmbracoMembershipProvider() == false) + { + //it's a generic provider so update the locked out property based on our known constant alias + var isLockedOutProperty = result.SelectMany(x => x.Properties).FirstOrDefault(x => x.Alias == Constants.Conventions.Member.IsLockedOut); + if (isLockedOutProperty != null && isLockedOutProperty.Value.ToString() != "1") + { + isLockedOutProperty.View = "readonlyvalue"; + isLockedOutProperty.Value = _localizedTextService.Localize("general/no"); + } + + return result; + } + else + { + var umbracoProvider = (IUmbracoMemberTypeMembershipProvider) provider; + + //This is kind of a hack because a developer is supposed to be allowed to set their property editor - would have been much easier + // if we just had all of the membeship provider fields on the member table :( + // TODO: But is there a way to map the IMember.IsLockedOut to the property ? i dunno. + var isLockedOutProperty = result.SelectMany(x => x.Properties).FirstOrDefault(x => x.Alias == umbracoProvider.LockPropertyTypeAlias); + if (isLockedOutProperty != null && isLockedOutProperty.Value.ToString() != "1") + { + isLockedOutProperty.View = "readonlyvalue"; + isLockedOutProperty.Value = _localizedTextService.Localize("general/no"); + } + + return result; + } + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Web/Models/Mapping/MembershipScenarioResolver.cs b/src/Umbraco.Web/Models/Mapping/MembershipScenarioResolver.cs new file mode 100644 index 0000000000..508aa3cd6d --- /dev/null +++ b/src/Umbraco.Web/Models/Mapping/MembershipScenarioResolver.cs @@ -0,0 +1,33 @@ +using System; +using Umbraco.Core; +using Umbraco.Core.Models; +using Umbraco.Core.Models.Membership; +using Umbraco.Core.Security; +using Umbraco.Core.Services; + +namespace Umbraco.Web.Models.Mapping +{ + internal class MembershipScenarioResolver + { + private readonly Lazy _memberTypeService; + + public MembershipScenarioResolver(Lazy memberTypeService) + { + _memberTypeService = memberTypeService; + } + + public MembershipScenario Resolve(IMember source) + { + var provider = Core.Security.MembershipProviderExtensions.GetMembersMembershipProvider(); + + if (provider.IsUmbracoMembershipProvider()) + { + return MembershipScenario.NativeUmbraco; + } + var memberType = _memberTypeService.Value.Get(Constants.Conventions.MemberTypes.DefaultAlias); + return memberType != null + ? MembershipScenario.CustomProviderWithUmbracoLink + : MembershipScenario.StandaloneCustomProvider; + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Web/Models/Mapping/OwnerResolver.cs b/src/Umbraco.Web/Models/Mapping/OwnerResolver.cs index 1bc8fe60ee..dd87942dc1 100644 --- a/src/Umbraco.Web/Models/Mapping/OwnerResolver.cs +++ b/src/Umbraco.Web/Models/Mapping/OwnerResolver.cs @@ -1,6 +1,5 @@ using AutoMapper; using Umbraco.Core.Models; -using Umbraco.Core.Models.EntityBase; using Umbraco.Core.Models.Membership; using Umbraco.Core.Services; using Umbraco.Web.Models.ContentEditing; @@ -11,7 +10,7 @@ namespace Umbraco.Web.Models.Mapping /// Maps the Owner for IContentBase /// /// - internal class OwnerResolver : ValueResolver + internal class OwnerResolver where TPersisted : IContentBase { private readonly IUserService _userService; @@ -21,7 +20,7 @@ namespace Umbraco.Web.Models.Mapping _userService = userService; } - protected override UserBasic ResolveCore(TPersisted source) + public UserBasic Resolve(TPersisted source) { return Mapper.Map(source.GetCreatorProfile(_userService)); } diff --git a/src/Umbraco.Web/Models/Mapping/PreValueDisplayResolver.cs b/src/Umbraco.Web/Models/Mapping/PreValueDisplayResolver.cs index 0770bd2637..18b1c477f4 100644 --- a/src/Umbraco.Web/Models/Mapping/PreValueDisplayResolver.cs +++ b/src/Umbraco.Web/Models/Mapping/PreValueDisplayResolver.cs @@ -12,7 +12,7 @@ using Umbraco.Web.Models.ContentEditing; namespace Umbraco.Web.Models.Mapping { - internal class PreValueDisplayResolver : ValueResolver> + internal class PreValueDisplayResolver { private readonly Lazy _dataTypeService; @@ -43,7 +43,7 @@ namespace Umbraco.Web.Models.Mapping } } - internal IEnumerable Convert(IDataTypeDefinition source) + public IEnumerable Resolve(IDataTypeDefinition source) { PropertyEditor propEd = null; if (source.PropertyEditorAlias.IsNullOrWhiteSpace() == false) @@ -72,10 +72,5 @@ namespace Umbraco.Web.Models.Mapping return result; } - - protected override IEnumerable ResolveCore(IDataTypeDefinition source) - { - return Convert(source); - } } } \ No newline at end of file diff --git a/src/Umbraco.Web/Models/Mapping/PropertyGroupDisplayResolver.cs b/src/Umbraco.Web/Models/Mapping/PropertyGroupDisplayResolver.cs index 2f11eb04c4..799b576fd9 100644 --- a/src/Umbraco.Web/Models/Mapping/PropertyGroupDisplayResolver.cs +++ b/src/Umbraco.Web/Models/Mapping/PropertyGroupDisplayResolver.cs @@ -5,12 +5,12 @@ using Umbraco.Web.Models.ContentEditing; namespace Umbraco.Web.Models.Mapping { - internal class PropertyGroupDisplayResolver : ValueResolver>> + internal class PropertyGroupDisplayResolver where TSource : ContentTypeSave where TPropertyTypeDestination : PropertyTypeDisplay where TPropertyTypeSource : PropertyTypeBasic { - protected override IEnumerable> ResolveCore(TSource source) + public IEnumerable> Resolve(TSource source) { return source.Groups.Select(Mapper.Map>); } diff --git a/src/Umbraco.Web/Models/Mapping/PropertyTypeGroupResolver.cs b/src/Umbraco.Web/Models/Mapping/PropertyTypeGroupResolver.cs index c53a7c48b3..5255b239b7 100644 --- a/src/Umbraco.Web/Models/Mapping/PropertyTypeGroupResolver.cs +++ b/src/Umbraco.Web/Models/Mapping/PropertyTypeGroupResolver.cs @@ -10,7 +10,7 @@ using Umbraco.Web.Models.ContentEditing; namespace Umbraco.Web.Models.Mapping { - internal class PropertyTypeGroupResolver : ValueResolver>> + internal class PropertyTypeGroupResolver where TPropertyType : PropertyTypeDisplay, new() { private readonly PropertyEditorCollection _propertyEditors; @@ -62,7 +62,7 @@ namespace Umbraco.Web.Models.Mapping .FirstOrDefault(x => x != null); } - protected override IEnumerable> ResolveCore(IContentTypeComposition source) + public IEnumerable> Resolve(IContentTypeComposition source) { // deal with groups var groups = new List>(); diff --git a/src/Umbraco.Web/Models/Mapping/RelationModelMapper.cs b/src/Umbraco.Web/Models/Mapping/RelationProfile.cs similarity index 53% rename from src/Umbraco.Web/Models/Mapping/RelationModelMapper.cs rename to src/Umbraco.Web/Models/Mapping/RelationProfile.cs index 8b15f18f30..629ee17751 100644 --- a/src/Umbraco.Web/Models/Mapping/RelationModelMapper.cs +++ b/src/Umbraco.Web/Models/Mapping/RelationProfile.cs @@ -1,20 +1,19 @@ using AutoMapper; using Umbraco.Core.Models; -using Umbraco.Core.Models.Mapping; using Relation = Umbraco.Web.Models.ContentEditing.Relation; using RelationType = Umbraco.Web.Models.ContentEditing.RelationType; namespace Umbraco.Web.Models.Mapping { - internal class RelationModelMapper : ModelMapperConfiguration + internal class RelationProfile : Profile { - public override void ConfigureMappings(IMapperConfiguration config) + public RelationProfile() { //FROM IRelationType TO RelationType - config.CreateMap(); + CreateMap(); //FROM IRelation TO Relation - config.CreateMap(); + CreateMap(); } } } \ No newline at end of file diff --git a/src/Umbraco.Web/Models/Mapping/SectionModelMapper.cs b/src/Umbraco.Web/Models/Mapping/SectionProfile.cs similarity index 62% rename from src/Umbraco.Web/Models/Mapping/SectionModelMapper.cs rename to src/Umbraco.Web/Models/Mapping/SectionProfile.cs index 61cd649185..c7ee644235 100644 --- a/src/Umbraco.Web/Models/Mapping/SectionModelMapper.cs +++ b/src/Umbraco.Web/Models/Mapping/SectionProfile.cs @@ -1,27 +1,23 @@ -using System.Collections.Generic; -using AutoMapper; -using Umbraco.Core.Services; -using Umbraco.Core.Models.Mapping; -using Umbraco.Web.Models.ContentEditing; - -namespace Umbraco.Web.Models.Mapping -{ - internal class SectionModelMapper : ModelMapperConfiguration - { - private readonly ILocalizedTextService _textService; - - public SectionModelMapper(ILocalizedTextService textService) - { - _textService = textService; - } - - public override void ConfigureMappings(IMapperConfiguration config) - { - config.CreateMap() - .ForMember( - dto => dto.Name, - expression => expression.MapFrom(section => _textService.Localize("sections/" + section.Alias, (IDictionary)null))) - .ReverseMap(); //backwards too! - } - } +using System.Collections.Generic; +using AutoMapper; +using Umbraco.Core.Services; +using Umbraco.Web.Models.ContentEditing; + +namespace Umbraco.Web.Models.Mapping +{ + internal class SectionProfile : Profile + { + private readonly ILocalizedTextService _textService; + + public SectionProfile(ILocalizedTextService textService) + { + _textService = textService; + + CreateMap() + .ForMember( + dto => dto.Name, + expression => expression.MapFrom(section => _textService.Localize("sections/" + section.Alias, (IDictionary)null))) + .ReverseMap(); //backwards too! + } + } } \ No newline at end of file diff --git a/src/Umbraco.Web/Models/Mapping/TabModelMapper.cs b/src/Umbraco.Web/Models/Mapping/TabModelMapper.cs deleted file mode 100644 index 31749b3a09..0000000000 --- a/src/Umbraco.Web/Models/Mapping/TabModelMapper.cs +++ /dev/null @@ -1,14 +0,0 @@ -using AutoMapper; -using Umbraco.Core.Models; -using Umbraco.Core.Models.Mapping; - -namespace Umbraco.Web.Models.Mapping -{ - internal class TabModelMapper : ModelMapperConfiguration - { - public override void ConfigureMappings(IMapperConfiguration config) - { - config.CreateMap(); - } - } -} \ No newline at end of file diff --git a/src/Umbraco.Web/Models/Mapping/TabProfile.cs b/src/Umbraco.Web/Models/Mapping/TabProfile.cs new file mode 100644 index 0000000000..b6c36764c2 --- /dev/null +++ b/src/Umbraco.Web/Models/Mapping/TabProfile.cs @@ -0,0 +1,13 @@ +using AutoMapper; +using Umbraco.Core.Models; + +namespace Umbraco.Web.Models.Mapping +{ + internal class TabProfile : Profile + { + public TabProfile() + { + CreateMap(); + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Web/Models/Mapping/TabsAndPropertiesResolver.cs b/src/Umbraco.Web/Models/Mapping/TabsAndPropertiesResolver.cs index cfee99ba5f..321cef3ecc 100644 --- a/src/Umbraco.Web/Models/Mapping/TabsAndPropertiesResolver.cs +++ b/src/Umbraco.Web/Models/Mapping/TabsAndPropertiesResolver.cs @@ -4,10 +4,8 @@ using System.Linq; using AutoMapper; using Umbraco.Core; using Umbraco.Core.Models; -using Umbraco.Core.PropertyEditors; using Umbraco.Core.Services; using Umbraco.Web.Models.ContentEditing; -using umbraco; using Umbraco.Web.Composing; namespace Umbraco.Web.Models.Mapping @@ -15,7 +13,7 @@ namespace Umbraco.Web.Models.Mapping /// /// Creates the tabs collection with properties assigned for display models /// - internal class TabsAndPropertiesResolver : ValueResolver>> + internal class TabsAndPropertiesResolver { private readonly ILocalizedTextService _localizedTextService; protected IEnumerable IgnoreProperties { get; set; } @@ -219,7 +217,7 @@ namespace Umbraco.Web.Models.Mapping display.Tabs = tabs; } - protected override IEnumerable> ResolveCore(IContentBase content) + public virtual IEnumerable> Resolve(IContentBase content) { var tabs = new List>(); diff --git a/src/Umbraco.Web/Models/Mapping/TemplateModelMapper.cs b/src/Umbraco.Web/Models/Mapping/TemplateProfile.cs similarity index 76% rename from src/Umbraco.Web/Models/Mapping/TemplateModelMapper.cs rename to src/Umbraco.Web/Models/Mapping/TemplateProfile.cs index 6be4257b7a..6647a1d548 100644 --- a/src/Umbraco.Web/Models/Mapping/TemplateModelMapper.cs +++ b/src/Umbraco.Web/Models/Mapping/TemplateProfile.cs @@ -1,18 +1,17 @@ using AutoMapper; using Umbraco.Core.Models; -using Umbraco.Core.Models.Mapping; using Umbraco.Web.Models.ContentEditing; namespace Umbraco.Web.Models.Mapping { - internal class TemplateModelMapper : ModelMapperConfiguration + internal class TemplateProfile : Profile { - public override void ConfigureMappings(IMapperConfiguration config) + public TemplateProfile() { - config.CreateMap() + CreateMap() .ForMember(x => x.Notifications, exp => exp.Ignore()); - config.CreateMap() + CreateMap() .ForMember(x => x.DeletedDate, exp => exp.Ignore()) .ForMember(x => x.Key, exp => exp.Ignore()) .ForMember(x => x.Path, exp => exp.Ignore()) diff --git a/src/Umbraco.Web/Models/Mapping/UserModelMapper.cs b/src/Umbraco.Web/Models/Mapping/UserProfile.cs similarity index 86% rename from src/Umbraco.Web/Models/Mapping/UserModelMapper.cs rename to src/Umbraco.Web/Models/Mapping/UserProfile.cs index ea39490f3a..bdfe82bfd7 100644 --- a/src/Umbraco.Web/Models/Mapping/UserModelMapper.cs +++ b/src/Umbraco.Web/Models/Mapping/UserProfile.cs @@ -1,77 +1,73 @@ -using System; -using AutoMapper; -using Umbraco.Core; -using Umbraco.Core.Models.Mapping; -using Umbraco.Core.Models.Membership; -using Umbraco.Web.Models.ContentEditing; -using Umbraco.Core.Models; -using Umbraco.Core.Models.Identity; -using Umbraco.Core.Security; -using Umbraco.Core.Services; - -namespace Umbraco.Web.Models.Mapping -{ - internal class UserModelMapper : ModelMapperConfiguration - { - private readonly ILocalizedTextService _textService; - - public UserModelMapper(ILocalizedTextService textService) - { - _textService = textService; - } - - public override void ConfigureMappings(IMapperConfiguration config) - { - config.CreateMap() - .ForMember(detail => detail.UserId, opt => opt.MapFrom(user => GetIntId(user.Id))) - .ForMember(detail => detail.UserType, opt => opt.MapFrom(user => user.UserType.Alias)) - .ForMember(detail => detail.StartContentId, opt => opt.MapFrom(user => user.StartContentId)) - .ForMember(detail => detail.StartMediaId, opt => opt.MapFrom(user => user.StartMediaId)) - .ForMember(detail => detail.Culture, opt => opt.MapFrom(user => user.GetUserCulture(_textService))) - .ForMember( - detail => detail.EmailHash, - opt => opt.MapFrom(user => user.Email.ToLowerInvariant().Trim().ToMd5())) - .ForMember(detail => detail.SecondsUntilTimeout, opt => opt.Ignore()); - - config.CreateMap() - .ForMember(detail => detail.UserId, opt => opt.MapFrom(user => user.Id)) - .ForMember(detail => detail.UserType, opt => opt.MapFrom(user => user.UserTypeAlias)) - .ForMember(detail => detail.StartContentId, opt => opt.MapFrom(user => user.StartContentId)) - .ForMember(detail => detail.StartMediaId, opt => opt.MapFrom(user => user.StartMediaId)) - .ForMember(detail => detail.Culture, opt => opt.MapFrom(user => user.Culture)) - .ForMember(detail => detail.AllowedSections, opt => opt.MapFrom(user => user.AllowedSections)) - .ForMember( - detail => detail.EmailHash, - opt => opt.MapFrom(user => user.Email.ToLowerInvariant().Trim().ToMd5())) - .ForMember(detail => detail.SecondsUntilTimeout, opt => opt.Ignore()); - - config.CreateMap() - .ForMember(detail => detail.UserId, opt => opt.MapFrom(profile => GetIntId(profile.Id))); - - config.CreateMap() - .ConstructUsing((IUser user) => new UserData()) - .ForMember(detail => detail.Id, opt => opt.MapFrom(user => user.Id)) - .ForMember(detail => detail.AllowedApplications, opt => opt.MapFrom(user => user.AllowedSections)) - .ForMember(detail => detail.RealName, opt => opt.MapFrom(user => user.Name)) - .ForMember(detail => detail.Roles, opt => opt.MapFrom(user => new[] {user.UserType.Alias})) - .ForMember(detail => detail.StartContentNode, opt => opt.MapFrom(user => user.StartContentId)) - .ForMember(detail => detail.StartMediaNode, opt => opt.MapFrom(user => user.StartMediaId)) - .ForMember(detail => detail.Username, opt => opt.MapFrom(user => user.Username)) - .ForMember(detail => detail.Culture, opt => opt.MapFrom(user => user.GetUserCulture(_textService))) - .ForMember(detail => detail.SessionId, opt => opt.MapFrom(user => user.SecurityStamp.IsNullOrWhiteSpace() ? Guid.NewGuid().ToString("N") : user.SecurityStamp)); - - } - - private static int GetIntId(object id) - { - var result = id.TryConvertTo(); - if (result.Success == false) - { - throw new InvalidOperationException( - "Cannot convert the profile to a " + typeof(UserDetail).Name + " object since the id is not an integer"); - } - return result.Result; - } - - } +using System; +using AutoMapper; +using Umbraco.Core; +using Umbraco.Core.Models.Membership; +using Umbraco.Web.Models.ContentEditing; +using Umbraco.Core.Models; +using Umbraco.Core.Models.Identity; +using Umbraco.Core.Security; +using Umbraco.Core.Services; + +namespace Umbraco.Web.Models.Mapping +{ + internal class UserProfile : Profile + { + private readonly ILocalizedTextService _textService; + + public UserProfile(ILocalizedTextService textService) + { + _textService = textService; + + CreateMap() + .ForMember(detail => detail.UserId, opt => opt.MapFrom(user => GetIntId(user.Id))) + .ForMember(detail => detail.UserType, opt => opt.MapFrom(user => user.UserType.Alias)) + .ForMember(detail => detail.StartContentId, opt => opt.MapFrom(user => user.StartContentId)) + .ForMember(detail => detail.StartMediaId, opt => opt.MapFrom(user => user.StartMediaId)) + .ForMember(detail => detail.Culture, opt => opt.MapFrom(user => user.GetUserCulture(_textService))) + .ForMember( + detail => detail.EmailHash, + opt => opt.MapFrom(user => user.Email.ToLowerInvariant().Trim().ToMd5())) + .ForMember(detail => detail.SecondsUntilTimeout, opt => opt.Ignore()); + + CreateMap() + .ForMember(detail => detail.UserId, opt => opt.MapFrom(user => user.Id)) + .ForMember(detail => detail.UserType, opt => opt.MapFrom(user => user.UserTypeAlias)) + .ForMember(detail => detail.StartContentId, opt => opt.MapFrom(user => user.StartContentId)) + .ForMember(detail => detail.StartMediaId, opt => opt.MapFrom(user => user.StartMediaId)) + .ForMember(detail => detail.Culture, opt => opt.MapFrom(user => user.Culture)) + .ForMember(detail => detail.AllowedSections, opt => opt.MapFrom(user => user.AllowedSections)) + .ForMember( + detail => detail.EmailHash, + opt => opt.MapFrom(user => user.Email.ToLowerInvariant().Trim().ToMd5())) + .ForMember(detail => detail.SecondsUntilTimeout, opt => opt.Ignore()); + + CreateMap() + .ForMember(detail => detail.UserId, opt => opt.MapFrom(profile => GetIntId(profile.Id))); + + CreateMap() + .ConstructUsing((IUser user) => new UserData()) + .ForMember(detail => detail.Id, opt => opt.MapFrom(user => user.Id)) + .ForMember(detail => detail.AllowedApplications, opt => opt.MapFrom(user => user.AllowedSections)) + .ForMember(detail => detail.RealName, opt => opt.MapFrom(user => user.Name)) + .ForMember(detail => detail.Roles, opt => opt.MapFrom(user => new[] {user.UserType.Alias})) + .ForMember(detail => detail.StartContentNode, opt => opt.MapFrom(user => user.StartContentId)) + .ForMember(detail => detail.StartMediaNode, opt => opt.MapFrom(user => user.StartMediaId)) + .ForMember(detail => detail.Username, opt => opt.MapFrom(user => user.Username)) + .ForMember(detail => detail.Culture, opt => opt.MapFrom(user => user.GetUserCulture(_textService))) + .ForMember(detail => detail.SessionId, opt => opt.MapFrom(user => user.SecurityStamp.IsNullOrWhiteSpace() ? Guid.NewGuid().ToString("N") : user.SecurityStamp)); + + } + + private static int GetIntId(object id) + { + var result = id.TryConvertTo(); + if (result.Success == false) + { + throw new InvalidOperationException( + "Cannot convert the profile to a " + typeof(UserDetail).Name + " object since the id is not an integer"); + } + return result.Result; + } + + } } \ No newline at end of file diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index 474024b0d9..118b5c42b3 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -60,7 +60,7 @@ - + @@ -139,7 +139,7 @@ - + @@ -189,9 +189,14 @@ - + + - + + + + + @@ -414,7 +419,7 @@ - + @@ -560,10 +565,10 @@ - - - - + + + + @@ -603,8 +608,8 @@ - - + + @@ -762,15 +767,15 @@ - + - - - + + + - + - + diff --git a/src/Umbraco.Web/WebRuntimeComponent.cs b/src/Umbraco.Web/WebRuntimeComponent.cs index ad1106e4cd..38d6ff1649 100644 --- a/src/Umbraco.Web/WebRuntimeComponent.cs +++ b/src/Umbraco.Web/WebRuntimeComponent.cs @@ -53,7 +53,7 @@ namespace Umbraco.Web { base.Compose(composition); - composition.Container.RegisterFrom(); + composition.Container.RegisterFrom(); var pluginManager = composition.Container.GetInstance(); var logger = composition.Container.GetInstance();