Revert "Temp8 tinymce"
This commit is contained in:
@@ -1,44 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Services;
|
||||
|
||||
namespace Umbraco.Web.Models.Mapping
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates the list of action buttons allowed for this user - Publish, Send to publish, save, unpublish returned as the button's 'letter'
|
||||
/// </summary>
|
||||
internal class ActionButtonsResolver
|
||||
{
|
||||
public ActionButtonsResolver(IUserService userService, IContentService contentService)
|
||||
{
|
||||
UserService = userService;
|
||||
ContentService = contentService;
|
||||
}
|
||||
|
||||
private IUserService UserService { get; }
|
||||
private IContentService ContentService { get; }
|
||||
|
||||
public IEnumerable<string> Resolve(IContent source)
|
||||
{
|
||||
//cannot check permissions without a context
|
||||
if (UmbracoContext.Current == null)
|
||||
return Enumerable.Empty<string>();
|
||||
|
||||
string path;
|
||||
if (source.HasIdentity)
|
||||
path = source.Path;
|
||||
else
|
||||
{
|
||||
var parent = ContentService.GetById(source.ParentId);
|
||||
path = parent == null ? "-1" : parent.Path;
|
||||
}
|
||||
|
||||
//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 :(
|
||||
return UserService.GetPermissionsForPath(UmbracoContext.Current.Security.CurrentUser, path).GetAllPermissions();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
using AutoMapper;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Web.Models.ContentEditing;
|
||||
|
||||
namespace Umbraco.Web.Models.Mapping
|
||||
{
|
||||
internal class AuditMapperProfile : Profile
|
||||
{
|
||||
public AuditMapperProfile()
|
||||
{
|
||||
CreateMap<IAuditItem, AuditLog>()
|
||||
.ForMember(log => log.UserAvatars, expression => expression.Ignore())
|
||||
.ForMember(log => log.UserName, expression => expression.Ignore())
|
||||
.ForMember(log => log.NodeId, expression => expression.MapFrom(item => item.Id))
|
||||
.ForMember(log => log.Timestamp, expression => expression.MapFrom(item => item.CreateDate))
|
||||
.ForMember(log => log.LogType, expression => expression.MapFrom(item => item.AuditType));
|
||||
}
|
||||
}
|
||||
}
|
||||
49
src/Umbraco.Web/Models/Mapping/AutoMapperExtensions.cs
Normal file
49
src/Umbraco.Web/Models/Mapping/AutoMapperExtensions.cs
Normal file
@@ -0,0 +1,49 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using AutoMapper;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Web.Models.ContentEditing;
|
||||
|
||||
namespace Umbraco.Web.Models.Mapping
|
||||
{
|
||||
internal static class AutoMapperExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// This maps an object and passes in the current <see cref="UmbracoContext"/> so the mapping logic can use it
|
||||
/// </summary>
|
||||
/// <typeparam name="TIn"></typeparam>
|
||||
/// <typeparam name="TOut"></typeparam>
|
||||
/// <param name="obj"></param>
|
||||
/// <param name="umbCtx"></param>
|
||||
/// <returns></returns>
|
||||
public static TOut MapWithUmbracoContext<TIn, TOut>(TIn obj, UmbracoContext umbCtx)
|
||||
{
|
||||
return Mapper.Map<TIn, TOut>(obj, opt => opt.Items["UmbracoContext"] = umbCtx);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns an <see cref="UmbracoContext"/> from the mapping options
|
||||
/// </summary>
|
||||
/// <param name="res"></param>
|
||||
/// <returns></returns>
|
||||
/// <remarks>
|
||||
/// If an UmbracoContext is not found in the mapping options, it will try to retrieve it from the singleton
|
||||
/// </remarks>
|
||||
public static UmbracoContext GetUmbracoContext(this ResolutionContext res)
|
||||
{
|
||||
//get the context from the mapping options set during a mapping operation
|
||||
object umbCtx;
|
||||
if (res.Options.Items.TryGetValue("UmbracoContext", out umbCtx))
|
||||
{
|
||||
var umbracoContext = umbCtx as UmbracoContext;
|
||||
if (umbracoContext != null) return umbracoContext;
|
||||
}
|
||||
|
||||
//return the singleton (this could be null)
|
||||
return UmbracoContext.Current;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,28 +1,34 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using AutoMapper;
|
||||
using Umbraco.Core.Configuration.UmbracoSettings;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Web.Composing;
|
||||
using Umbraco.Web.Models.ContentEditing;
|
||||
|
||||
namespace Umbraco.Web.Models.Mapping
|
||||
{
|
||||
internal class AvailablePropertyEditorsResolver
|
||||
{
|
||||
private readonly IContentSection _contentSection;
|
||||
|
||||
public AvailablePropertyEditorsResolver(IContentSection contentSection)
|
||||
{
|
||||
_contentSection = contentSection;
|
||||
}
|
||||
|
||||
public IEnumerable<PropertyEditorBasic> Resolve(IDataType source)
|
||||
{
|
||||
return Current.PropertyEditors
|
||||
.Where(x => !x.IsDeprecated || _contentSection.ShowDeprecatedPropertyEditors || source.EditorAlias == x.Alias)
|
||||
.OrderBy(x => x.Name)
|
||||
.Select(Mapper.Map<PropertyEditorBasic>);
|
||||
}
|
||||
}
|
||||
}
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using AutoMapper;
|
||||
using Umbraco.Core.Configuration.UmbracoSettings;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.PropertyEditors;
|
||||
using Umbraco.Web.Models.ContentEditing;
|
||||
|
||||
namespace Umbraco.Web.Models.Mapping
|
||||
{
|
||||
internal class AvailablePropertyEditorsResolver : ValueResolver<IDataTypeDefinition, IEnumerable<PropertyEditorBasic>>
|
||||
{
|
||||
private readonly IContentSection _contentSection;
|
||||
|
||||
public AvailablePropertyEditorsResolver(IContentSection contentSection)
|
||||
{
|
||||
_contentSection = contentSection;
|
||||
}
|
||||
|
||||
protected override IEnumerable<PropertyEditorBasic> ResolveCore(IDataTypeDefinition source)
|
||||
{
|
||||
return PropertyEditorResolver.Current.PropertyEditors
|
||||
.Where(x =>
|
||||
{
|
||||
if (_contentSection.ShowDeprecatedPropertyEditors)
|
||||
return true;
|
||||
return source.PropertyEditorAlias == x.Alias || x.IsDeprecated == false;
|
||||
})
|
||||
.OrderBy(x => x.Name)
|
||||
.Select(Mapper.Map<PropertyEditorBasic>);
|
||||
}
|
||||
}
|
||||
}
|
||||
53
src/Umbraco.Web/Models/Mapping/CodeFileDisplayMapper.cs
Normal file
53
src/Umbraco.Web/Models/Mapping/CodeFileDisplayMapper.cs
Normal file
@@ -0,0 +1,53 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
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 : MapperConfiguration
|
||||
{
|
||||
public override void ConfigureMappings(IConfiguration config, ApplicationContext applicationContext)
|
||||
{
|
||||
config.CreateMap<IPartialView, CodeFileDisplay>()
|
||||
.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<Script, CodeFileDisplay>()
|
||||
.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<CodeFileDisplay, IPartialView>()
|
||||
.IgnoreDeletableEntityCommonProperties()
|
||||
.ForMember(x => x.Id, exp => exp.Ignore())
|
||||
.ForMember(x => x.Key, exp => exp.Ignore())
|
||||
.ForMember(x => x.Path, exp => exp.Ignore())
|
||||
.ForMember(x => x.Path, exp => exp.Ignore())
|
||||
.ForMember(x => x.Alias, exp => exp.Ignore())
|
||||
.ForMember(x => x.Name, exp => exp.Ignore())
|
||||
.ForMember(x => x.OriginalPath, exp => exp.Ignore())
|
||||
.ForMember(x => x.HasIdentity, exp => exp.Ignore());
|
||||
|
||||
config.CreateMap<CodeFileDisplay, Script>()
|
||||
.IgnoreDeletableEntityCommonProperties()
|
||||
.ForMember(x => x.Id, exp => exp.Ignore())
|
||||
.ForMember(x => x.Key, exp => exp.Ignore())
|
||||
.ForMember(x => x.Path, exp => exp.Ignore())
|
||||
.ForMember(x => x.Path, exp => exp.Ignore())
|
||||
.ForMember(x => x.Alias, exp => exp.Ignore())
|
||||
.ForMember(x => x.Name, exp => exp.Ignore())
|
||||
.ForMember(x => x.OriginalPath, exp => exp.Ignore())
|
||||
.ForMember(x => x.HasIdentity, exp => exp.Ignore());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
using AutoMapper;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Web.Models.ContentEditing;
|
||||
using Stylesheet = Umbraco.Core.Models.Stylesheet;
|
||||
|
||||
namespace Umbraco.Web.Models.Mapping
|
||||
{
|
||||
public class CodeFileMapperProfile : Profile
|
||||
{
|
||||
public CodeFileMapperProfile()
|
||||
{
|
||||
CreateMap<IPartialView, CodeFileDisplay>()
|
||||
.ForMember(dest => dest.FileType, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.Notifications, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.Path, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.Snippet, opt => opt.Ignore());
|
||||
|
||||
CreateMap<Script, CodeFileDisplay>()
|
||||
.ForMember(dest => dest.FileType, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.Notifications, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.Path, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.Snippet, opt => opt.Ignore());
|
||||
|
||||
CreateMap<Stylesheet, CodeFileDisplay>()
|
||||
.ForMember(dest => dest.FileType, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.Notifications, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.Path, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.Snippet, opt => opt.Ignore());
|
||||
|
||||
CreateMap<CodeFileDisplay, IPartialView>()
|
||||
.IgnoreEntityCommonProperties()
|
||||
.ForMember(dest => dest.Id, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.Key, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.Path, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.Path, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.Alias, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.Name, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.OriginalPath, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.HasIdentity, opt => opt.Ignore());
|
||||
|
||||
CreateMap<CodeFileDisplay, Script>()
|
||||
.IgnoreEntityCommonProperties()
|
||||
.ForMember(dest => dest.Id, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.Key, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.Path, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.Path, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.Alias, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.Name, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.OriginalPath, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.HasIdentity, opt => opt.Ignore());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using AutoMapper;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Models.ContentEditing;
|
||||
using Umbraco.Web.ContentApps;
|
||||
using Umbraco.Web.Models.ContentEditing;
|
||||
|
||||
namespace Umbraco.Web.Models.Mapping
|
||||
{
|
||||
// injected into ContentMapperProfile,
|
||||
// maps ContentApps when mapping IContent to ContentItemDisplay
|
||||
internal class ContentAppResolver : IValueResolver<IContent, ContentItemDisplay, IEnumerable<ContentApp>>
|
||||
{
|
||||
private readonly ContentAppDefinitionCollection _contentAppDefinitions;
|
||||
|
||||
public ContentAppResolver(ContentAppDefinitionCollection contentAppDefinitions)
|
||||
{
|
||||
_contentAppDefinitions = contentAppDefinitions;
|
||||
}
|
||||
|
||||
public IEnumerable<ContentApp> Resolve(IContent source, ContentItemDisplay destination, IEnumerable<ContentApp> destMember, ResolutionContext context)
|
||||
{
|
||||
return _contentAppDefinitions.GetContentAppsFor(source);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
using AutoMapper;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Services;
|
||||
using Umbraco.Web.Models.ContentEditing;
|
||||
|
||||
namespace Umbraco.Web.Models.Mapping
|
||||
{
|
||||
internal class ContentChildOfListViewResolver : IValueResolver<IContent, ContentItemDisplay, bool>
|
||||
{
|
||||
private readonly IContentService _contentService;
|
||||
private readonly IContentTypeService _contentTypeService;
|
||||
|
||||
public ContentChildOfListViewResolver(IContentService contentService, IContentTypeService contentTypeService)
|
||||
{
|
||||
_contentService = contentService;
|
||||
_contentTypeService = contentTypeService;
|
||||
}
|
||||
|
||||
public bool Resolve(IContent source, ContentItemDisplay destination, bool destMember, ResolutionContext context)
|
||||
{
|
||||
// map the IsChildOfListView (this is actually if it is a descendant of a list view!)
|
||||
var parent = _contentService.GetParent(source);
|
||||
return parent != null && (parent.ContentType.IsContainer || _contentTypeService.HasContainerInPath(parent.Path));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
//using AutoMapper;
|
||||
//using Umbraco.Core;
|
||||
//using Umbraco.Core.Models;
|
||||
//using Umbraco.Web.Models.ContentEditing;
|
||||
//using ContentVariation = Umbraco.Core.Models.ContentVariation;
|
||||
|
||||
// fixme remove this file?
|
||||
//namespace Umbraco.Web.Models.Mapping
|
||||
//{
|
||||
// /// <summary>
|
||||
// /// Used to map the <see cref="TabbedContentItem{ContentPropertyDisplay, IContent}"/> name from an <see cref="IContent"/> depending on it's variation settings
|
||||
// /// </summary>
|
||||
// internal class ContentItemDisplayNameResolver : IValueResolver<IContent, ContentVariationDisplay, string>
|
||||
// {
|
||||
// public string Resolve(IContent source, ContentVariationDisplay destination, string destMember, ResolutionContext context)
|
||||
// {
|
||||
// var culture = context.GetCulture();
|
||||
// return source.ContentType.VariesByCulture() && culture != null
|
||||
// ? source.GetCultureName(culture)
|
||||
// : source.Name;
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
@@ -1,73 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using AutoMapper;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Services;
|
||||
using Umbraco.Web.Models.ContentEditing;
|
||||
using Language = Umbraco.Web.Models.ContentEditing.Language;
|
||||
|
||||
namespace Umbraco.Web.Models.Mapping
|
||||
{
|
||||
internal class ContentVariantResolver : IValueResolver<IContent, ContentItemDisplay, IEnumerable<ContentVariantDisplay>>
|
||||
{
|
||||
private readonly ILocalizationService _localizationService;
|
||||
|
||||
public ContentVariantResolver(ILocalizationService localizationService)
|
||||
{
|
||||
_localizationService = localizationService ?? throw new ArgumentNullException(nameof(localizationService));
|
||||
}
|
||||
|
||||
public IEnumerable<ContentVariantDisplay> Resolve(IContent source, ContentItemDisplay destination, IEnumerable<ContentVariantDisplay> destMember, ResolutionContext context)
|
||||
{
|
||||
var result = new List<ContentVariantDisplay>();
|
||||
if (!source.ContentType.VariesByCulture())
|
||||
{
|
||||
//this is invariant so just map the IContent instance to ContentVariationDisplay
|
||||
result.Add(context.Mapper.Map<ContentVariantDisplay>(source));
|
||||
}
|
||||
else
|
||||
{
|
||||
var allLanguages = _localizationService.GetAllLanguages().OrderBy(x => x.Id).ToList();
|
||||
if (allLanguages.Count == 0) return Enumerable.Empty<ContentVariantDisplay>(); //this should never happen
|
||||
|
||||
var langs = context.Mapper.Map<IEnumerable<ILanguage>, IEnumerable<Language>>(allLanguages, null, context).ToList();
|
||||
|
||||
//create a variant for each lang, then we'll populate the values
|
||||
var variants = langs.Select(x =>
|
||||
{
|
||||
//We need to set the culture in the mapping context since this is needed to ensure that the correct property values
|
||||
//are resolved during the mapping
|
||||
context.Options.SetCulture(x.IsoCode);
|
||||
return context.Mapper.Map<IContent, ContentVariantDisplay>(source, null, context);
|
||||
}).ToList();
|
||||
|
||||
for (int i = 0; i < langs.Count; i++)
|
||||
{
|
||||
var x = langs[i];
|
||||
var variant = variants[i];
|
||||
|
||||
variant.Language = x;
|
||||
variant.Name = source.GetCultureName(x.IsoCode);
|
||||
}
|
||||
|
||||
//Put the default language first in the list & then sort rest by a-z
|
||||
var defaultLang = variants.SingleOrDefault(x => x.Language.IsDefault);
|
||||
|
||||
//Remove the default lang from the list for now
|
||||
variants.Remove(defaultLang);
|
||||
|
||||
//Sort the remaining languages a-z
|
||||
variants = variants.OrderBy(x => x.Name).ToList();
|
||||
|
||||
//Insert the default lang as the first item
|
||||
variants.Insert(0, defaultLang);
|
||||
|
||||
return variants;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,144 +0,0 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using AutoMapper;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Services;
|
||||
using Umbraco.Web.Models.ContentEditing;
|
||||
using Umbraco.Web.Trees;
|
||||
|
||||
namespace Umbraco.Web.Models.Mapping
|
||||
{
|
||||
/// <summary>
|
||||
/// Declares how model mappings for content
|
||||
/// </summary>
|
||||
internal class ContentMapperProfile : Profile
|
||||
{
|
||||
public ContentMapperProfile(
|
||||
ContentUrlResolver contentUrlResolver,
|
||||
ContentTreeNodeUrlResolver<IContent, ContentTreeController> contentTreeNodeUrlResolver,
|
||||
TabsAndPropertiesResolver<IContent, ContentVariantDisplay> tabsAndPropertiesResolver,
|
||||
ContentAppResolver contentAppResolver,
|
||||
IUserService userService,
|
||||
IContentService contentService,
|
||||
IContentTypeService contentTypeService,
|
||||
ILocalizationService localizationService)
|
||||
{
|
||||
// create, capture, cache
|
||||
var contentOwnerResolver = new OwnerResolver<IContent>(userService);
|
||||
var creatorResolver = new CreatorResolver(userService);
|
||||
var actionButtonsResolver = new ActionButtonsResolver(userService, contentService);
|
||||
var childOfListViewResolver = new ContentChildOfListViewResolver(contentService, contentTypeService);
|
||||
var contentTypeBasicResolver = new ContentTypeBasicResolver<IContent, ContentItemDisplay>();
|
||||
var defaultTemplateResolver = new DefaultTemplateResolver();
|
||||
var variantResolver = new ContentVariantResolver(localizationService);
|
||||
var schedPublishReleaseDateResolver = new ScheduledPublishDateResolver(ContentScheduleAction.Release);
|
||||
var schedPublishExpireDateResolver = new ScheduledPublishDateResolver(ContentScheduleAction.Expire);
|
||||
|
||||
//FROM IContent TO ContentItemDisplay
|
||||
CreateMap<IContent, ContentItemDisplay>()
|
||||
.ForMember(dest => dest.Udi, opt => opt.MapFrom(src => Udi.Create(src.Blueprint ? Constants.UdiEntityType.DocumentBlueprint : 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.Variants, opt => opt.ResolveUsing(variantResolver))
|
||||
.ForMember(dest => dest.ContentApps, opt => opt.ResolveUsing(contentAppResolver))
|
||||
.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.IsBlueprint, opt => opt.MapFrom(src => src.Blueprint))
|
||||
.ForMember(dest => dest.IsChildOfListView, opt => opt.ResolveUsing(childOfListViewResolver))
|
||||
.ForMember(dest => dest.Trashed, opt => opt.MapFrom(src => src.Trashed))
|
||||
.ForMember(dest => dest.TemplateAlias, opt => opt.ResolveUsing(defaultTemplateResolver))
|
||||
.ForMember(dest => dest.Urls, opt => opt.ResolveUsing(contentUrlResolver))
|
||||
.ForMember(dest => dest.AllowPreview, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.TreeNodeUrl, opt => opt.ResolveUsing(contentTreeNodeUrlResolver))
|
||||
.ForMember(dest => dest.Notifications, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.Errors, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.DocumentType, opt => opt.ResolveUsing(contentTypeBasicResolver))
|
||||
.ForMember(dest => dest.AllowedTemplates, opt =>
|
||||
opt.MapFrom(content => content.ContentType.AllowedTemplates
|
||||
.Where(t => t.Alias.IsNullOrWhiteSpace() == false && t.Name.IsNullOrWhiteSpace() == false)
|
||||
.ToDictionary(t => t.Alias, t => t.Name)))
|
||||
.ForMember(dest => dest.AllowedActions, opt => opt.ResolveUsing(src => actionButtonsResolver.Resolve(src)))
|
||||
.ForMember(dest => dest.AdditionalData, opt => opt.Ignore());
|
||||
|
||||
CreateMap<IContent, ContentVariantDisplay>()
|
||||
.ForMember(dest => dest.PublishDate, opt => opt.MapFrom(src => src.PublishDate))
|
||||
.ForMember(dest => dest.ReleaseDate, opt => opt.ResolveUsing(schedPublishReleaseDateResolver))
|
||||
.ForMember(dest => dest.ExpireDate, opt => opt.ResolveUsing(schedPublishExpireDateResolver))
|
||||
.ForMember(dest => dest.Segment, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.Language, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.Notifications, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.State, opt => opt.ResolveUsing<ContentSavedStateResolver<ContentPropertyDisplay>>())
|
||||
.ForMember(dest => dest.Tabs, opt => opt.ResolveUsing(tabsAndPropertiesResolver));
|
||||
|
||||
//FROM IContent TO ContentItemBasic<ContentPropertyBasic, IContent>
|
||||
CreateMap<IContent, ContentItemBasic<ContentPropertyBasic>>()
|
||||
.ForMember(dest => dest.Udi, opt => opt.MapFrom(src =>
|
||||
Udi.Create(src.Blueprint ? Constants.UdiEntityType.DocumentBlueprint : 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.ContentTypeAlias, opt => opt.MapFrom(src => src.ContentType.Alias))
|
||||
.ForMember(dest => dest.Alias, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.AdditionalData, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.UpdateDate, opt => opt.ResolveUsing<UpdateDateResolver>())
|
||||
.ForMember(dest => dest.Name, opt => opt.ResolveUsing<NameResolver>())
|
||||
.ForMember(dest => dest.State, opt => opt.ResolveUsing<ContentBasicSavedStateResolver<ContentPropertyBasic>>())
|
||||
.ForMember(dest => dest.VariesByCulture, opt => opt.MapFrom(src => src.ContentType.VariesByCulture()));
|
||||
|
||||
//FROM IContent TO ContentPropertyCollectionDto
|
||||
//NOTE: the property mapping for cultures relies on a culture being set in the mapping context
|
||||
CreateMap<IContent, ContentPropertyCollectionDto>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resolves the update date for a content item/content variant
|
||||
/// </summary>
|
||||
private class UpdateDateResolver : IValueResolver<IContent, ContentItemBasic<ContentPropertyBasic>, DateTime>
|
||||
{
|
||||
public DateTime Resolve(IContent source, ContentItemBasic<ContentPropertyBasic> destination, DateTime destMember, ResolutionContext context)
|
||||
{
|
||||
// invariant = global date
|
||||
if (!source.ContentType.VariesByCulture()) return source.UpdateDate;
|
||||
|
||||
// variant = depends on culture
|
||||
var culture = context.Options.GetCulture();
|
||||
|
||||
// if there's no culture here, the issue is somewhere else (UI, whatever) - throw!
|
||||
if (culture == null)
|
||||
throw new InvalidOperationException("Missing culture in mapping options.");
|
||||
|
||||
// if we don't have a date for a culture, it means the culture is not available, and
|
||||
// hey we should probably not be mapping it, but it's too late, return a fallback date
|
||||
var date = source.GetUpdateDate(culture);
|
||||
return date ?? source.UpdateDate;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resolves the name for a content item/content variant
|
||||
/// </summary>
|
||||
private class NameResolver : IValueResolver<IContent, ContentItemBasic<ContentPropertyBasic>, string>
|
||||
{
|
||||
public string Resolve(IContent source, ContentItemBasic<ContentPropertyBasic> destination, string destMember, ResolutionContext context)
|
||||
{
|
||||
// invariant = only 1 name
|
||||
if (!source.ContentType.VariesByCulture()) return source.Name;
|
||||
|
||||
// variant = depends on culture
|
||||
var culture = context.Options.GetCulture();
|
||||
|
||||
// if there's no culture here, the issue is somewhere else (UI, whatever) - throw!
|
||||
if (culture == null)
|
||||
throw new InvalidOperationException("Missing culture in mapping options.");
|
||||
|
||||
// if we don't have a name for a culture, it means the culture is not available, and
|
||||
// hey we should probably not be mapping it, but it's too late, return a fallback name
|
||||
return source.CultureInfos.TryGetValue(culture, out var name) && !name.Name.IsNullOrWhiteSpace() ? name.Name : $"(({source.Name}))";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
212
src/Umbraco.Web/Models/Mapping/ContentModelMapper.cs
Normal file
212
src/Umbraco.Web/Models/Mapping/ContentModelMapper.cs
Normal file
@@ -0,0 +1,212 @@
|
||||
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.Models.Mapping;
|
||||
using Umbraco.Core.Services;
|
||||
using Umbraco.Web.Models.ContentEditing;
|
||||
using Umbraco.Web.Trees;
|
||||
using Umbraco.Web.Routing;
|
||||
using umbraco.BusinessLogic.Actions;
|
||||
using Umbraco.Core.PropertyEditors;
|
||||
using Content = Umbraco.Core.Models.Content;
|
||||
|
||||
namespace Umbraco.Web.Models.Mapping
|
||||
{
|
||||
/// <summary>
|
||||
/// Declares how model mappings for content
|
||||
/// </summary>
|
||||
internal class ContentModelMapper : MapperConfiguration
|
||||
{
|
||||
public override void ConfigureMappings(IConfiguration config, ApplicationContext applicationContext)
|
||||
{
|
||||
|
||||
//FROM IContent TO ContentItemDisplay
|
||||
config.CreateMap<IContent, ContentItemDisplay>()
|
||||
.ForMember(display => display.Udi, expression => expression.MapFrom(content => Udi.Create(content.IsBlueprint ? Constants.UdiEntityType.DocumentBlueprint : Constants.UdiEntityType.Document, content.Key)))
|
||||
.ForMember(display => display.Owner, expression => expression.ResolveUsing(new OwnerResolver<IContent>()))
|
||||
.ForMember(display => display.Updater, expression => expression.ResolveUsing(new CreatorResolver()))
|
||||
.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.ResolveUsing(new ChildOfListViewResolver(applicationContext.Services.ContentService, applicationContext.Services.ContentTypeService)))
|
||||
.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.ResolveUsing<DefaultTemplateResolver>())
|
||||
.ForMember(display => display.HasPublishedVersion, expression => expression.MapFrom(content => content.HasPublishedVersion))
|
||||
.ForMember(display => display.Urls, expression => expression.ResolveUsing<ContentUrlResolver>())
|
||||
.ForMember(display => display.Properties, expression => expression.Ignore())
|
||||
.ForMember(display => display.AllowPreview, expression => expression.Ignore())
|
||||
.ForMember(display => display.TreeNodeUrl, opt => opt.ResolveUsing(new ContentTreeNodeUrlResolver<IContent, ContentTreeController>()))
|
||||
.ForMember(display => display.Notifications, expression => expression.Ignore())
|
||||
.ForMember(display => display.Errors, expression => expression.Ignore())
|
||||
.ForMember(display => display.Alias, expression => expression.Ignore())
|
||||
.ForMember(display => display.DocumentType, expression => expression.ResolveUsing<ContentTypeBasicResolver>())
|
||||
.ForMember(display => display.AllowedTemplates, expression =>
|
||||
expression.MapFrom(content => content.ContentType.AllowedTemplates
|
||||
.Where(t => t.Alias.IsNullOrWhiteSpace() == false && t.Name.IsNullOrWhiteSpace() == false)
|
||||
.ToDictionary(t => t.Alias, t => t.Name)))
|
||||
.ForMember(display => display.Tabs, expression => expression.ResolveUsing(new TabsAndPropertiesResolver<IContent>(applicationContext.Services.TextService)))
|
||||
.ForMember(display => display.AllowedActions, expression => expression.ResolveUsing(
|
||||
new ActionButtonsResolver(new Lazy<IUserService>(() => applicationContext.Services.UserService), new Lazy<IContentService>(() => applicationContext.Services.ContentService))))
|
||||
.AfterMap((content, display) =>
|
||||
{
|
||||
if (content.ContentType.IsContainer)
|
||||
{
|
||||
TabsAndPropertiesResolver<IContent>.AddListView(display, "content", applicationContext.Services.DataTypeService, applicationContext.Services.TextService);
|
||||
}
|
||||
});
|
||||
|
||||
//FROM IContent TO ContentItemBasic<ContentPropertyBasic, IContent>
|
||||
config.CreateMap<IContent, ContentItemBasic<ContentPropertyBasic, IContent>>()
|
||||
.ForMember(display => display.Udi, expression => expression.MapFrom(content => Udi.Create(content.IsBlueprint ? Constants.UdiEntityType.DocumentBlueprint : Constants.UdiEntityType.Document, content.Key)))
|
||||
.ForMember(dto => dto.Owner, expression => expression.ResolveUsing(new OwnerResolver<IContent>()))
|
||||
.ForMember(dto => dto.Updater, expression => expression.ResolveUsing(new CreatorResolver()))
|
||||
.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<IContent>
|
||||
config.CreateMap<IContent, ContentItemDto<IContent>>()
|
||||
.ForMember(display => display.Udi, expression => expression.MapFrom(content => Udi.Create(content.IsBlueprint ? Constants.UdiEntityType.DocumentBlueprint : Constants.UdiEntityType.Document, content.Key)))
|
||||
.ForMember(dto => dto.Owner, expression => expression.ResolveUsing(new OwnerResolver<IContent>()))
|
||||
.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;
|
||||
}
|
||||
|
||||
internal class ContentUrlResolver : IValueResolver
|
||||
{
|
||||
public ResolutionResult Resolve(ResolutionResult source)
|
||||
{
|
||||
var content = (IContent)source.Value;
|
||||
|
||||
var umbCtx = source.Context.GetUmbracoContext();
|
||||
|
||||
var urls = umbCtx == null
|
||||
? new[] {"Cannot generate urls without a current Umbraco Context"}
|
||||
: content.GetContentUrls(umbCtx);
|
||||
|
||||
return source.New(urls, typeof(string[]));
|
||||
}
|
||||
}
|
||||
|
||||
internal class DefaultTemplateResolver : ValueResolver<IContent, string>
|
||||
{
|
||||
protected override string ResolveCore(IContent source)
|
||||
{
|
||||
if (source == null || source.Template == null) return null;
|
||||
|
||||
var alias = source.Template.Alias;
|
||||
|
||||
//set default template if template isn't set
|
||||
if (string.IsNullOrEmpty(alias))
|
||||
alias = source.ContentType.DefaultTemplate == null
|
||||
? string.Empty
|
||||
: source.ContentType.DefaultTemplate.Alias;
|
||||
|
||||
return alias;
|
||||
}
|
||||
}
|
||||
|
||||
private class ChildOfListViewResolver : ValueResolver<IContent, bool>
|
||||
{
|
||||
private readonly IContentService _contentService;
|
||||
private readonly IContentTypeService _contentTypeService;
|
||||
|
||||
public ChildOfListViewResolver(IContentService contentService, IContentTypeService contentTypeService)
|
||||
{
|
||||
_contentService = contentService;
|
||||
_contentTypeService = contentTypeService;
|
||||
}
|
||||
|
||||
protected override bool ResolveCore(IContent source)
|
||||
{
|
||||
// map the IsChildOfListView (this is actually if it is a descendant of a list view!)
|
||||
var parent = _contentService.GetParent(source);
|
||||
return parent != null && (parent.ContentType.IsContainer || _contentTypeService.HasContainerInPath(parent.Path));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resolves a <see cref="ContentTypeBasic"/> from the <see cref="IContent"/> item and checks if the current user
|
||||
/// has access to see this data
|
||||
/// </summary>
|
||||
private class ContentTypeBasicResolver : ValueResolver<IContent, ContentTypeBasic>
|
||||
{
|
||||
protected override ContentTypeBasic ResolveCore(IContent source)
|
||||
{
|
||||
//TODO: We can resolve the UmbracoContext from the IValueResolver options!
|
||||
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 contentTypeBasic = Mapper.Map<ContentTypeBasic>(source.ContentType);
|
||||
return contentTypeBasic;
|
||||
}
|
||||
//no access
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the list of action buttons allowed for this user - Publish, Send to publish, save, unpublish returned as the button's 'letter'
|
||||
/// </summary>
|
||||
private class ActionButtonsResolver : ValueResolver<IContent, IEnumerable<string>>
|
||||
{
|
||||
private readonly Lazy<IUserService> _userService;
|
||||
private readonly Lazy<IContentService> _contentService;
|
||||
|
||||
public ActionButtonsResolver(Lazy<IUserService> userService, Lazy<IContentService> contentService)
|
||||
{
|
||||
if (userService == null) throw new ArgumentNullException("userService");
|
||||
if (contentService == null) throw new ArgumentNullException("contentService");
|
||||
_userService = userService;
|
||||
_contentService = contentService;
|
||||
}
|
||||
|
||||
protected override IEnumerable<string> ResolveCore(IContent source)
|
||||
{
|
||||
if (UmbracoContext.Current == null)
|
||||
{
|
||||
//cannot check permissions without a context
|
||||
return Enumerable.Empty<string>();
|
||||
}
|
||||
var svc = _userService.Value;
|
||||
|
||||
string path;
|
||||
if (source.HasIdentity)
|
||||
path = source.Path;
|
||||
else
|
||||
{
|
||||
var parent = _contentService.Value.GetById(source.ParentId);
|
||||
path = parent == null ? "-1" : parent.Path;
|
||||
}
|
||||
|
||||
var permissions = svc.GetPermissionsForPath(
|
||||
//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,
|
||||
path)
|
||||
.GetAllPermissions();
|
||||
|
||||
return permissions;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,81 +1,75 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using AutoMapper;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.PropertyEditors;
|
||||
using Umbraco.Core.Services;
|
||||
using Umbraco.Web.Composing;
|
||||
using Umbraco.Web.Models.ContentEditing;
|
||||
using ContentVariation = Umbraco.Core.Models.ContentVariation;
|
||||
|
||||
namespace Umbraco.Web.Models.Mapping
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a base generic ContentPropertyBasic from a Property
|
||||
/// </summary>
|
||||
internal class ContentPropertyBasicConverter<TDestination> : ITypeConverter<Property, TDestination>
|
||||
where TDestination : ContentPropertyBasic, new()
|
||||
{
|
||||
private readonly ILogger _logger;
|
||||
private readonly PropertyEditorCollection _propertyEditors;
|
||||
protected IDataTypeService DataTypeService { get; }
|
||||
|
||||
public ContentPropertyBasicConverter(IDataTypeService dataTypeService, ILogger logger, PropertyEditorCollection propertyEditors)
|
||||
{
|
||||
_logger = logger;
|
||||
_propertyEditors = propertyEditors;
|
||||
DataTypeService = dataTypeService;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Assigns the PropertyEditor, Id, Alias and Value to the property
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public virtual TDestination Convert(Property property, TDestination dest, ResolutionContext context)
|
||||
{
|
||||
var editor = _propertyEditors[property.PropertyType.PropertyEditorAlias];
|
||||
if (editor == null)
|
||||
{
|
||||
_logger.Error<ContentPropertyBasicConverter<TDestination>>(
|
||||
new NullReferenceException("The property editor with alias " + property.PropertyType.PropertyEditorAlias + " does not exist"),
|
||||
"No property editor '{PropertyEditorAlias}' found, converting to a Label",
|
||||
property.PropertyType.PropertyEditorAlias);
|
||||
|
||||
editor = _propertyEditors[Constants.PropertyEditors.Aliases.NoEdit];
|
||||
}
|
||||
|
||||
var result = new TDestination
|
||||
{
|
||||
Id = property.Id,
|
||||
Alias = property.Alias,
|
||||
PropertyEditor = editor,
|
||||
Editor = editor.Alias
|
||||
};
|
||||
|
||||
// if there's a set of property aliases specified, we will check if the current property's value should be mapped.
|
||||
// if it isn't one of the ones specified in 'includeProperties', we will just return the result without mapping the Value.
|
||||
var includedProperties = context.Options.GetIncludedProperties();
|
||||
if (includedProperties != null && !includedProperties.Contains(property.Alias))
|
||||
return result;
|
||||
|
||||
//Get the culture from the context which will be set during the mapping operation for each property
|
||||
var culture = context.Options.GetCulture();
|
||||
|
||||
//a culture needs to be in the context for a property type that can vary
|
||||
if (culture == null && property.PropertyType.VariesByCulture())
|
||||
throw new InvalidOperationException($"No culture found in mapping operation when one is required for the culture variant property type {property.PropertyType.Alias}");
|
||||
|
||||
//set the culture to null if it's an invariant property type
|
||||
culture = !property.PropertyType.VariesByCulture() ? null : culture;
|
||||
|
||||
result.Culture = culture;
|
||||
|
||||
// if no 'IncludeProperties' were specified or this property is set to be included - we will map the value and return.
|
||||
result.Value = editor.GetValueEditor().ToEditor(property, DataTypeService, culture);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using AutoMapper;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.PropertyEditors;
|
||||
using Umbraco.Core.Services;
|
||||
using Umbraco.Web.Models.ContentEditing;
|
||||
|
||||
namespace Umbraco.Web.Models.Mapping
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a base generic ContentPropertyBasic from a Property
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
internal class ContentPropertyBasicConverter<T> : ITypeConverter<Property, T>
|
||||
where T : ContentPropertyBasic, new()
|
||||
{
|
||||
protected IDataTypeService DataTypeService { get; private set; }
|
||||
|
||||
private static readonly List<string> ComplexPropertyTypeAliases = new List<string> {"Umbraco.NestedContent"};
|
||||
|
||||
public ContentPropertyBasicConverter(IDataTypeService dataTypeService)
|
||||
{
|
||||
DataTypeService = dataTypeService;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Assigns the PropertyEditor, Id, Alias and Value to the property
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public virtual T Convert(ResolutionContext context)
|
||||
{
|
||||
var property = context.SourceValue as Property;
|
||||
if (property == null)
|
||||
throw new InvalidOperationException("Source value is not a property.");
|
||||
|
||||
var editor = PropertyEditorResolver.Current.GetByAlias(property.PropertyType.PropertyEditorAlias);
|
||||
if (editor == null)
|
||||
{
|
||||
LogHelper.Error<ContentPropertyBasicConverter<T>>(
|
||||
"No property editor found, converting to a Label",
|
||||
new NullReferenceException("The property editor with alias " +
|
||||
property.PropertyType.PropertyEditorAlias + " does not exist"));
|
||||
|
||||
editor = PropertyEditorResolver.Current.GetByAlias(Constants.PropertyEditors.NoEditAlias);
|
||||
}
|
||||
|
||||
var result = new T
|
||||
{
|
||||
Id = property.Id,
|
||||
Alias = property.Alias,
|
||||
PropertyEditor = editor,
|
||||
Editor = editor.Alias
|
||||
};
|
||||
|
||||
// if there's a set of property aliases specified, we will check if the current property's value should be mapped.
|
||||
// if it isn't one of the ones specified in 'includeProperties', we will just return the result without mapping the Value.
|
||||
if (context.Options.Items.ContainsKey("IncludeProperties"))
|
||||
{
|
||||
var includeProperties = context.Options.Items["IncludeProperties"] as IEnumerable<string>;
|
||||
if (includeProperties != null && includeProperties.Contains(property.Alias) == false)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
// if no 'IncludeProperties' were specified or this property is set to be included - we will map the value and return.
|
||||
result.Value = editor.ValueEditor.ConvertDbToEditor(property, property.PropertyType, DataTypeService);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,72 +1,67 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using AutoMapper;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Configuration;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.PropertyEditors;
|
||||
using Umbraco.Core.Services;
|
||||
using Umbraco.Web.Models.ContentEditing;
|
||||
|
||||
namespace Umbraco.Web.Models.Mapping
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a ContentPropertyDisplay from a Property
|
||||
/// </summary>
|
||||
internal class ContentPropertyDisplayConverter : ContentPropertyBasicConverter<ContentPropertyDisplay>
|
||||
{
|
||||
private readonly ILocalizedTextService _textService;
|
||||
|
||||
public ContentPropertyDisplayConverter(IDataTypeService dataTypeService, ILocalizedTextService textService, ILogger logger, PropertyEditorCollection propertyEditors)
|
||||
: base(dataTypeService, logger, propertyEditors)
|
||||
{
|
||||
_textService = textService;
|
||||
}
|
||||
public override ContentPropertyDisplay Convert(Property originalProp, ContentPropertyDisplay dest, ResolutionContext context)
|
||||
{
|
||||
var display = base.Convert(originalProp, dest, context);
|
||||
|
||||
var config = DataTypeService.GetDataType(originalProp.PropertyType.DataTypeId).Configuration;
|
||||
|
||||
// fixme - IDataValueEditor configuration - general issue
|
||||
// GetValueEditor() returns a non-configured IDataValueEditor
|
||||
// - for richtext and nested, configuration determines HideLabel, so we need to configure the value editor
|
||||
// - could configuration also determines ValueType, everywhere?
|
||||
// - does it make any sense to use a IDataValueEditor without configuring it?
|
||||
|
||||
// configure the editor for display with configuration
|
||||
var valEditor = display.PropertyEditor.GetValueEditor(config);
|
||||
|
||||
//set the display properties after mapping
|
||||
display.Alias = originalProp.Alias;
|
||||
display.Description = originalProp.PropertyType.Description;
|
||||
display.Label = originalProp.PropertyType.Name;
|
||||
display.HideLabel = valEditor.HideLabel;
|
||||
|
||||
//add the validation information
|
||||
display.Validation.Mandatory = originalProp.PropertyType.Mandatory;
|
||||
display.Validation.Pattern = originalProp.PropertyType.ValidationRegExp;
|
||||
|
||||
if (display.PropertyEditor == null)
|
||||
{
|
||||
//display.Config = PreValueCollection.AsDictionary(preVals);
|
||||
//if there is no property editor it means that it is a legacy data type
|
||||
// we cannot support editing with that so we'll just render the readonly value view.
|
||||
display.View = "views/propertyeditors/readonlyvalue/readonlyvalue.html";
|
||||
}
|
||||
else
|
||||
{
|
||||
//let the property editor format the pre-values
|
||||
display.Config = display.PropertyEditor.GetConfigurationEditor().ToValueEditor(config);
|
||||
display.View = valEditor.View;
|
||||
}
|
||||
|
||||
//Translate
|
||||
display.Label = _textService.UmbracoDictionaryTranslate(display.Label);
|
||||
display.Description = _textService.UmbracoDictionaryTranslate(display.Description);
|
||||
|
||||
return display;
|
||||
}
|
||||
}
|
||||
}
|
||||
using System;
|
||||
using AutoMapper;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Services;
|
||||
using Umbraco.Web.Models.ContentEditing;
|
||||
|
||||
namespace Umbraco.Web.Models.Mapping
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a ContentPropertyDisplay from a Property
|
||||
/// </summary>
|
||||
internal class ContentPropertyDisplayConverter : ContentPropertyBasicConverter<ContentPropertyDisplay>
|
||||
{
|
||||
private readonly ILocalizedTextService _textService;
|
||||
|
||||
public ContentPropertyDisplayConverter(IDataTypeService dataTypeService, ILocalizedTextService textService)
|
||||
: base(dataTypeService)
|
||||
{
|
||||
_textService = textService;
|
||||
}
|
||||
public override ContentPropertyDisplay Convert(ResolutionContext context)
|
||||
{
|
||||
var display = base.Convert(context);
|
||||
|
||||
var originalProperty = context.SourceValue as Property;
|
||||
if (originalProperty == null)
|
||||
throw new InvalidOperationException("Source value is not a property.");
|
||||
|
||||
var dataTypeService = DataTypeService;
|
||||
var preVals = dataTypeService.GetPreValuesCollectionByDataTypeId(originalProperty.PropertyType.DataTypeDefinitionId);
|
||||
|
||||
//configure the editor for display with the pre-values
|
||||
var valEditor = display.PropertyEditor.ValueEditor;
|
||||
valEditor.ConfigureForDisplay(preVals);
|
||||
|
||||
//set the display properties after mapping
|
||||
display.Alias = originalProperty.Alias;
|
||||
display.Description = originalProperty.PropertyType.Description;
|
||||
display.Label = originalProperty.PropertyType.Name;
|
||||
display.HideLabel = valEditor.HideLabel;
|
||||
|
||||
//add the validation information
|
||||
display.Validation.Mandatory = originalProperty.PropertyType.Mandatory;
|
||||
display.Validation.Pattern = originalProperty.PropertyType.ValidationRegExp;
|
||||
|
||||
if (display.PropertyEditor == null)
|
||||
{
|
||||
//display.Config = PreValueCollection.AsDictionary(preVals);
|
||||
//if there is no property editor it means that it is a legacy data type
|
||||
// we cannot support editing with that so we'll just render the readonly value view.
|
||||
display.View = "views/propertyeditors/readonlyvalue/readonlyvalue.html";
|
||||
}
|
||||
else
|
||||
{
|
||||
//let the property editor format the pre-values
|
||||
display.Config = display.PropertyEditor.PreValueEditor.ConvertDbToEditor(display.PropertyEditor.DefaultPreValues, preVals);
|
||||
display.View = valEditor.View;
|
||||
}
|
||||
|
||||
//Translate
|
||||
display.Label = _textService.UmbracoDictionaryTranslate(display.Label);
|
||||
display.Description = _textService.UmbracoDictionaryTranslate(display.Description);
|
||||
|
||||
return display;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,34 +1,41 @@
|
||||
using System;
|
||||
using AutoMapper;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.PropertyEditors;
|
||||
using Umbraco.Core.Services;
|
||||
using Umbraco.Web.Models.ContentEditing;
|
||||
|
||||
namespace Umbraco.Web.Models.Mapping
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a ContentPropertyDto from a Property
|
||||
/// </summary>
|
||||
internal class ContentPropertyDtoConverter : ContentPropertyBasicConverter<ContentPropertyDto>
|
||||
{
|
||||
public ContentPropertyDtoConverter(IDataTypeService dataTypeService, ILogger logger, PropertyEditorCollection propertyEditors)
|
||||
: base(dataTypeService, logger, propertyEditors)
|
||||
{ }
|
||||
|
||||
public override ContentPropertyDto Convert(Property property, ContentPropertyDto dest, ResolutionContext context)
|
||||
{
|
||||
var propertyDto = base.Convert(property, dest, context);
|
||||
|
||||
propertyDto.IsRequired = property.PropertyType.Mandatory;
|
||||
propertyDto.ValidationRegExp = property.PropertyType.ValidationRegExp;
|
||||
propertyDto.Description = property.PropertyType.Description;
|
||||
propertyDto.Label = property.PropertyType.Name;
|
||||
propertyDto.DataType = DataTypeService.GetDataType(property.PropertyType.DataTypeId);
|
||||
|
||||
return propertyDto;
|
||||
}
|
||||
}
|
||||
}
|
||||
using System;
|
||||
using AutoMapper;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Services;
|
||||
using Umbraco.Web.Models.ContentEditing;
|
||||
|
||||
namespace Umbraco.Web.Models.Mapping
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a ContentPropertyDto from a Property
|
||||
/// </summary>
|
||||
internal class ContentPropertyDtoConverter : ContentPropertyBasicConverter<ContentPropertyDto>
|
||||
{
|
||||
public ContentPropertyDtoConverter(IDataTypeService dataTypeService)
|
||||
: base(dataTypeService)
|
||||
{
|
||||
}
|
||||
|
||||
public override ContentPropertyDto Convert(ResolutionContext context)
|
||||
{
|
||||
var propertyDto = base.Convert(context);
|
||||
|
||||
var originalProperty = context.SourceValue as Property;
|
||||
if (originalProperty == null)
|
||||
throw new InvalidOperationException("Source value is not a property.");
|
||||
|
||||
var dataTypeService = DataTypeService;
|
||||
|
||||
propertyDto.IsRequired = originalProperty.PropertyType.Mandatory;
|
||||
propertyDto.ValidationRegExp = originalProperty.PropertyType.ValidationRegExp;
|
||||
propertyDto.Description = originalProperty.PropertyType.Description;
|
||||
propertyDto.Label = originalProperty.PropertyType.Name;
|
||||
|
||||
//TODO: We should be able to look both of these up at the same time!
|
||||
propertyDto.DataType = dataTypeService.GetDataTypeDefinitionById(originalProperty.PropertyType.DataTypeDefinitionId);
|
||||
propertyDto.PreValues = dataTypeService.GetPreValuesCollectionByDataTypeId(originalProperty.PropertyType.DataTypeDefinitionId);
|
||||
|
||||
return propertyDto;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
using AutoMapper;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.PropertyEditors;
|
||||
using Umbraco.Core.Services;
|
||||
using Umbraco.Web.Models.ContentEditing;
|
||||
|
||||
namespace Umbraco.Web.Models.Mapping
|
||||
{
|
||||
/// <summary>
|
||||
/// 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
|
||||
/// </summary>
|
||||
internal class ContentPropertyMapperProfile : Profile
|
||||
{
|
||||
public ContentPropertyMapperProfile(IDataTypeService dataTypeService, ILocalizedTextService textService, ILogger logger, PropertyEditorCollection propertyEditors)
|
||||
{
|
||||
var contentPropertyBasicConverter = new ContentPropertyBasicConverter<ContentPropertyBasic>(dataTypeService, logger, propertyEditors);
|
||||
var contentPropertyDtoConverter = new ContentPropertyDtoConverter(dataTypeService, logger, propertyEditors);
|
||||
var contentPropertyDisplayConverter = new ContentPropertyDisplayConverter(dataTypeService, textService, logger, propertyEditors);
|
||||
|
||||
//FROM Property TO ContentPropertyBasic
|
||||
CreateMap<PropertyGroup, Tab<ContentPropertyDisplay>>()
|
||||
.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())
|
||||
.ForMember(tab => tab.Expanded, expression => expression.Ignore());
|
||||
|
||||
//FROM Property TO ContentPropertyBasic
|
||||
CreateMap<Property, ContentPropertyBasic>().ConvertUsing(contentPropertyBasicConverter);
|
||||
|
||||
//FROM Property TO ContentPropertyDto
|
||||
CreateMap<Property, ContentPropertyDto>().ConvertUsing(contentPropertyDtoConverter);
|
||||
|
||||
//FROM Property TO ContentPropertyDisplay
|
||||
CreateMap<Property, ContentPropertyDisplay>().ConvertUsing(contentPropertyDisplayConverter);
|
||||
}
|
||||
}
|
||||
}
|
||||
37
src/Umbraco.Web/Models/Mapping/ContentPropertyModelMapper.cs
Normal file
37
src/Umbraco.Web/Models/Mapping/ContentPropertyModelMapper.cs
Normal file
@@ -0,0 +1,37 @@
|
||||
using AutoMapper;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Models.Mapping;
|
||||
using Umbraco.Web.Models.ContentEditing;
|
||||
|
||||
namespace Umbraco.Web.Models.Mapping
|
||||
{
|
||||
/// <summary>
|
||||
/// 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
|
||||
/// </summary>
|
||||
internal class ContentPropertyModelMapper : MapperConfiguration
|
||||
{
|
||||
public override void ConfigureMappings(IConfiguration config, ApplicationContext applicationContext)
|
||||
{
|
||||
//FROM Property TO ContentPropertyBasic
|
||||
config.CreateMap<PropertyGroup, Tab<ContentPropertyDisplay>>()
|
||||
.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<Property, ContentPropertyBasic>()
|
||||
.ConvertUsing(new ContentPropertyBasicConverter<ContentPropertyBasic>(applicationContext.Services.DataTypeService));
|
||||
|
||||
//FROM Property TO ContentPropertyDto
|
||||
config.CreateMap<Property, ContentPropertyDto>()
|
||||
.ConvertUsing(new ContentPropertyDtoConverter(applicationContext.Services.DataTypeService));
|
||||
|
||||
//FROM Property TO ContentPropertyDisplay
|
||||
config.CreateMap<Property, ContentPropertyDisplay>()
|
||||
.ConvertUsing(new ContentPropertyDisplayConverter(applicationContext.Services.DataTypeService, applicationContext.Services.TextService));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,78 +0,0 @@
|
||||
using System;
|
||||
using AutoMapper;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Web.Models.ContentEditing;
|
||||
|
||||
namespace Umbraco.Web.Models.Mapping
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Returns the <see cref="ContentSavedState?"/> for an <see cref="IContent"/> item
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
internal class ContentBasicSavedStateResolver<T> : IValueResolver<IContent, IContentProperties<T>, ContentSavedState?>
|
||||
where T : ContentPropertyBasic
|
||||
{
|
||||
private readonly ContentSavedStateResolver<T> _inner = new ContentSavedStateResolver<T>();
|
||||
|
||||
public ContentSavedState? Resolve(IContent source, IContentProperties<T> destination, ContentSavedState? destMember, ResolutionContext context)
|
||||
{
|
||||
return _inner.Resolve(source, destination, default, context);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the <see cref="ContentSavedState"/> for an <see cref="IContent"/> item
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
internal class ContentSavedStateResolver<T> : IValueResolver<IContent, IContentProperties<T>, ContentSavedState>
|
||||
where T : ContentPropertyBasic
|
||||
{
|
||||
public ContentSavedState Resolve(IContent source, IContentProperties<T> destination, ContentSavedState destMember, ResolutionContext context)
|
||||
{
|
||||
PublishedState publishedState;
|
||||
bool isEdited;
|
||||
bool isCreated;
|
||||
|
||||
if (source.ContentType.VariesByCulture())
|
||||
{
|
||||
//Get the culture from the context which will be set during the mapping operation for each variant
|
||||
var culture = context.Options.GetCulture();
|
||||
|
||||
//a culture needs to be in the context for a variant content item
|
||||
if (culture == null)
|
||||
throw new InvalidOperationException($"No culture found in mapping operation when one is required for a culture variant");
|
||||
|
||||
publishedState = source.PublishedState == PublishedState.Unpublished //if the entire document is unpublished, then flag every variant as unpublished
|
||||
? PublishedState.Unpublished
|
||||
: source.IsCulturePublished(culture)
|
||||
? PublishedState.Published
|
||||
: PublishedState.Unpublished;
|
||||
|
||||
isEdited = source.IsCultureEdited(culture);
|
||||
isCreated = source.Id > 0 && source.IsCultureAvailable(culture);
|
||||
}
|
||||
else
|
||||
{
|
||||
publishedState = source.PublishedState == PublishedState.Unpublished
|
||||
? PublishedState.Unpublished
|
||||
: PublishedState.Published;
|
||||
|
||||
isEdited = source.Edited;
|
||||
isCreated = source.Id > 0;
|
||||
}
|
||||
|
||||
if (!isCreated)
|
||||
return ContentSavedState.NotCreated;
|
||||
|
||||
if (publishedState == PublishedState.Unpublished)
|
||||
return ContentSavedState.Draft;
|
||||
|
||||
if (publishedState == PublishedState.Published)
|
||||
return isEdited ? ContentSavedState.PublishedPendingChanges : ContentSavedState.Published;
|
||||
|
||||
throw new NotSupportedException($"PublishedState {publishedState} is not supported.");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,24 +8,27 @@ namespace Umbraco.Web.Models.Mapping
|
||||
/// <summary>
|
||||
/// Gets the tree node url for the content or media
|
||||
/// </summary>
|
||||
internal class ContentTreeNodeUrlResolver<TSource, TController> : IValueResolver<TSource, object, string>
|
||||
internal class ContentTreeNodeUrlResolver<TSource, TController> : IValueResolver
|
||||
where TSource : IContentBase
|
||||
where TController : ContentTreeControllerBase
|
||||
{
|
||||
private readonly IUmbracoContextAccessor _umbracoContextAccessor;
|
||||
|
||||
public ContentTreeNodeUrlResolver(IUmbracoContextAccessor umbracoContextAccessor)
|
||||
public ResolutionResult Resolve(ResolutionResult source)
|
||||
{
|
||||
_umbracoContextAccessor = umbracoContextAccessor ?? throw new System.ArgumentNullException(nameof(umbracoContextAccessor));
|
||||
return source.New(ResolveCore(source, (TSource)source.Value), typeof(string));
|
||||
}
|
||||
|
||||
public string Resolve(TSource source, object destination, string destMember, ResolutionContext context)
|
||||
private string ResolveCore(ResolutionResult res, TSource source)
|
||||
{
|
||||
var umbracoContext = _umbracoContextAccessor.UmbracoContext;
|
||||
if (umbracoContext == null) return null;
|
||||
|
||||
var urlHelper = new UrlHelper(umbracoContext.HttpContext.Request.RequestContext);
|
||||
return urlHelper.GetUmbracoApiService<TController>(controller => controller.GetTreeNode(source.Key.ToString("N"), null));
|
||||
var umbCtx = res.Context.GetUmbracoContext();
|
||||
//map the tree node url
|
||||
if (umbCtx != null)
|
||||
{
|
||||
var urlHelper = new UrlHelper(umbCtx.HttpContext.Request.RequestContext);
|
||||
var url = urlHelper.GetUmbracoApiService<TController>(controller => controller.GetTreeNode(source.Key.ToString("N"), null));
|
||||
return url;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Web;
|
||||
using AutoMapper;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Web.Models.ContentEditing;
|
||||
|
||||
namespace Umbraco.Web.Models.Mapping
|
||||
{
|
||||
/// <summary>
|
||||
/// Resolves a <see cref="ContentTypeBasic"/> from the <see cref="IContent"/> item and checks if the current user
|
||||
/// has access to see this data
|
||||
/// </summary>
|
||||
internal class ContentTypeBasicResolver<TSource, TDestination> : IValueResolver<TSource, TDestination, ContentTypeBasic>
|
||||
where TSource : IContentBase
|
||||
{
|
||||
public ContentTypeBasic Resolve(TSource source, TDestination destination, ContentTypeBasic destMember, ResolutionContext context)
|
||||
{
|
||||
//TODO: We can resolve the UmbracoContext from the IValueResolver options!
|
||||
// OMG
|
||||
if (HttpContext.Current != null && UmbracoContext.Current != null && UmbracoContext.Current.Security.CurrentUser != null
|
||||
&& UmbracoContext.Current.Security.CurrentUser.AllowedSections.Any(x => x.Equals(Constants.Applications.Settings)))
|
||||
{
|
||||
ContentTypeBasic contentTypeBasic;
|
||||
if (source is IContent content)
|
||||
contentTypeBasic = Mapper.Map<IContentType, ContentTypeBasic>(content.ContentType);
|
||||
else if (source is IMedia media)
|
||||
contentTypeBasic = Mapper.Map<IMediaType, ContentTypeBasic>(media.ContentType);
|
||||
else
|
||||
throw new NotSupportedException($"Expected TSource to be IContent or IMedia, got {typeof(TSource).Name}.");
|
||||
|
||||
return contentTypeBasic;
|
||||
}
|
||||
//no access
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,263 +0,0 @@
|
||||
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;
|
||||
using ContentVariation = Umbraco.Core.Models.ContentVariation;
|
||||
|
||||
namespace Umbraco.Web.Models.Mapping
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines mappings for content/media/members type mappings
|
||||
/// </summary>
|
||||
internal class ContentTypeMapperProfile : Profile
|
||||
{
|
||||
public ContentTypeMapperProfile(PropertyEditorCollection propertyEditors, IDataTypeService dataTypeService, IFileService fileService, IContentTypeService contentTypeService, IMediaTypeService mediaTypeService)
|
||||
{
|
||||
CreateMap<DocumentTypeSave, IContentType>()
|
||||
//do the base mapping
|
||||
.MapBaseContentTypeSaveToEntity<DocumentTypeSave, PropertyTypeBasic, IContentType>()
|
||||
.ConstructUsing((source) => new ContentType(source.ParentId))
|
||||
.ForMember(source => source.AllowedTemplates, opt => opt.Ignore())
|
||||
.ForMember(dto => dto.DefaultTemplate, opt => opt.Ignore())
|
||||
.AfterMap((source, dest) =>
|
||||
{
|
||||
dest.AllowedTemplates = source.AllowedTemplates
|
||||
.Where(x => x != null)
|
||||
.Select(fileService.GetTemplate)
|
||||
.Where(x => x != null)
|
||||
.ToArray();
|
||||
|
||||
if (source.DefaultTemplate != null)
|
||||
dest.SetDefaultTemplate(fileService.GetTemplate(source.DefaultTemplate));
|
||||
else
|
||||
dest.SetDefaultTemplate(null);
|
||||
|
||||
ContentTypeProfileExtensions.AfterMapContentTypeSaveToEntity(source, dest, contentTypeService);
|
||||
});
|
||||
|
||||
CreateMap<MediaTypeSave, IMediaType>()
|
||||
//do the base mapping
|
||||
.MapBaseContentTypeSaveToEntity<MediaTypeSave, PropertyTypeBasic, IMediaType>()
|
||||
.ConstructUsing((source) => new MediaType(source.ParentId))
|
||||
.AfterMap((source, dest) =>
|
||||
{
|
||||
ContentTypeProfileExtensions.AfterMapMediaTypeSaveToEntity(source, dest, mediaTypeService);
|
||||
});
|
||||
|
||||
CreateMap<MemberTypeSave, IMemberType>()
|
||||
//do the base mapping
|
||||
.MapBaseContentTypeSaveToEntity<MemberTypeSave, MemberPropertyTypeBasic, IMemberType>()
|
||||
.ConstructUsing(source => new MemberType(source.ParentId))
|
||||
.AfterMap((source, dest) =>
|
||||
{
|
||||
ContentTypeProfileExtensions.AfterMapContentTypeSaveToEntity(source, dest, contentTypeService);
|
||||
|
||||
//map the MemberCanEditProperty,MemberCanViewProperty,IsSensitiveData
|
||||
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);
|
||||
dest.SetIsSensitiveProperty(localCopy.Alias, localCopy.IsSensitiveData);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
CreateMap<IContentTypeComposition, string>().ConvertUsing(dest => dest.Alias);
|
||||
|
||||
CreateMap<IMemberType, MemberTypeDisplay>()
|
||||
//map base logic
|
||||
.MapBaseContentTypeEntityToDisplay<IMemberType, MemberTypeDisplay, MemberPropertyTypeDisplay>(propertyEditors, dataTypeService, contentTypeService)
|
||||
.AfterMap((memberType, display) =>
|
||||
{
|
||||
//map the MemberCanEditProperty,MemberCanViewProperty,IsSensitiveData
|
||||
foreach (var propertyType in memberType.PropertyTypes)
|
||||
{
|
||||
var localCopy = propertyType;
|
||||
var displayProp = display.Groups.SelectMany(dest => dest.Properties).SingleOrDefault(dest => dest.Alias.InvariantEquals(localCopy.Alias));
|
||||
if (displayProp != null)
|
||||
{
|
||||
displayProp.MemberCanEditProperty = memberType.MemberCanEditProperty(localCopy.Alias);
|
||||
displayProp.MemberCanViewProperty = memberType.MemberCanViewProperty(localCopy.Alias);
|
||||
displayProp.IsSensitiveData = memberType.IsSensitiveProperty(localCopy.Alias);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
CreateMap<IMediaType, MediaTypeDisplay>()
|
||||
//map base logic
|
||||
.MapBaseContentTypeEntityToDisplay<IMediaType, MediaTypeDisplay, PropertyTypeDisplay>(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.GetDataType(name) != null)
|
||||
dest.ListViewEditorName = name;
|
||||
}
|
||||
});
|
||||
|
||||
CreateMap<IContentType, DocumentTypeDisplay>()
|
||||
//map base logic
|
||||
.MapBaseContentTypeEntityToDisplay<IContentType, DocumentTypeDisplay, PropertyTypeDisplay>(propertyEditors, dataTypeService, contentTypeService)
|
||||
.ForMember(dto => dto.AllowedTemplates, opt => opt.Ignore())
|
||||
.ForMember(dto => dto.DefaultTemplate, opt => opt.Ignore())
|
||||
.ForMember(display => display.Notifications, opt => opt.Ignore())
|
||||
.ForMember(display => display.AllowCultureVariant, opt => opt.MapFrom(type => type.VariesByCulture()))
|
||||
.AfterMap((source, dest) =>
|
||||
{
|
||||
//sync templates
|
||||
dest.AllowedTemplates = source.AllowedTemplates.Select(Mapper.Map<EntityBasic>).ToArray();
|
||||
|
||||
if (source.DefaultTemplate != null)
|
||||
dest.DefaultTemplate = Mapper.Map<EntityBasic>(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.GetDataType(name) != null)
|
||||
dest.ListViewEditorName = name;
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
CreateMap<IMemberType, ContentTypeBasic>()
|
||||
.ForMember(dest => dest.Udi, opt => opt.MapFrom(source => Udi.Create(Constants.UdiEntityType.MemberType, source.Key)))
|
||||
.ForMember(dest => dest.Blueprints, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.AdditionalData, opt => opt.Ignore());
|
||||
CreateMap<IMediaType, ContentTypeBasic>()
|
||||
.ForMember(dest => dest.Udi, opt => opt.MapFrom(source => Udi.Create(Constants.UdiEntityType.MediaType, source.Key)))
|
||||
.ForMember(dest => dest.Blueprints, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.AdditionalData, opt => opt.Ignore());
|
||||
CreateMap<IContentType, ContentTypeBasic>()
|
||||
.ForMember(dest => dest.Udi, opt => opt.MapFrom(source => Udi.Create(Constants.UdiEntityType.DocumentType, source.Key)))
|
||||
.ForMember(dest => dest.Blueprints, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.AdditionalData, opt => opt.Ignore());
|
||||
|
||||
CreateMap<PropertyTypeBasic, PropertyType>()
|
||||
|
||||
.ConstructUsing(propertyTypeBasic =>
|
||||
{
|
||||
var dataType = dataTypeService.GetDataType(propertyTypeBasic.DataTypeId);
|
||||
if (dataType == null) throw new NullReferenceException("No data type found with id " + propertyTypeBasic.DataTypeId);
|
||||
return new PropertyType(dataType, propertyTypeBasic.Alias);
|
||||
})
|
||||
|
||||
.IgnoreEntityCommonProperties()
|
||||
|
||||
.ForMember(dest => dest.IsPublishing, opt => opt.Ignore())
|
||||
|
||||
// see note above - have to do this here?
|
||||
.ForMember(dest => dest.PropertyEditorAlias, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.DeleteDate, opt => opt.Ignore())
|
||||
|
||||
.ForMember(dto => dto.Variations, opt => opt.ResolveUsing<PropertyTypeVariationsResolver>())
|
||||
|
||||
//only map if it is actually set
|
||||
.ForMember(dest => dest.Id, opt => opt.Condition(source => source.Id > 0))
|
||||
//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, opt => opt.Condition(source => source.GroupId > 0))
|
||||
.ForMember(dest => dest.PropertyGroupId, opt => opt.MapFrom(display => new Lazy<int>(() => display.GroupId, false)))
|
||||
.ForMember(dest => dest.Key, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.HasIdentity, opt => opt.Ignore())
|
||||
//ignore because this is set in the ctor NOT ON UPDATE, STUPID!
|
||||
//.ForMember(type => type.Alias, opt => opt.Ignore())
|
||||
//ignore because this is obsolete and shouldn't be used
|
||||
.ForMember(dest => dest.DataTypeId, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.Mandatory, opt => opt.MapFrom(source => source.Validation.Mandatory))
|
||||
.ForMember(dest => dest.ValidationRegExp, opt => opt.MapFrom(source => source.Validation.Pattern))
|
||||
.ForMember(dest => dest.DataTypeId, opt => opt.MapFrom(source => source.DataTypeId))
|
||||
.ForMember(dest => dest.Name, opt => opt.MapFrom(source => source.Label));
|
||||
|
||||
#region *** Used for mapping on top of an existing display object from a save object ***
|
||||
|
||||
CreateMap<MemberTypeSave, MemberTypeDisplay>()
|
||||
.MapBaseContentTypeSaveToDisplay<MemberTypeSave, MemberPropertyTypeBasic, MemberTypeDisplay, MemberPropertyTypeDisplay>();
|
||||
|
||||
CreateMap<MediaTypeSave, MediaTypeDisplay>()
|
||||
.MapBaseContentTypeSaveToDisplay<MediaTypeSave, PropertyTypeBasic, MediaTypeDisplay, PropertyTypeDisplay>();
|
||||
|
||||
CreateMap<DocumentTypeSave, DocumentTypeDisplay>()
|
||||
.MapBaseContentTypeSaveToDisplay<DocumentTypeSave, PropertyTypeBasic, DocumentTypeDisplay, PropertyTypeDisplay>()
|
||||
.ForMember(dto => dto.AllowedTemplates, opt => opt.Ignore())
|
||||
.ForMember(dto => dto.DefaultTemplate, opt => opt.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<EntityBasic>(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<EntityBasic>(template);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
dest.DefaultTemplate = null;
|
||||
}
|
||||
});
|
||||
|
||||
//for doc types, media types
|
||||
CreateMap<PropertyGroupBasic<PropertyTypeBasic>, PropertyGroup>()
|
||||
.MapPropertyGroupBasicToPropertyGroupPersistence<PropertyGroupBasic<PropertyTypeBasic>, PropertyTypeBasic>();
|
||||
|
||||
//for members
|
||||
CreateMap<PropertyGroupBasic<MemberPropertyTypeBasic>, PropertyGroup>()
|
||||
.MapPropertyGroupBasicToPropertyGroupPersistence<PropertyGroupBasic<MemberPropertyTypeBasic>, MemberPropertyTypeBasic>();
|
||||
|
||||
//for doc types, media types
|
||||
CreateMap<PropertyGroupBasic<PropertyTypeBasic>, PropertyGroupDisplay<PropertyTypeDisplay>>()
|
||||
.MapPropertyGroupBasicToPropertyGroupDisplay<PropertyGroupBasic<PropertyTypeBasic>, PropertyTypeBasic, PropertyTypeDisplay>();
|
||||
|
||||
//for members
|
||||
CreateMap<PropertyGroupBasic<MemberPropertyTypeBasic>, PropertyGroupDisplay<MemberPropertyTypeDisplay>>()
|
||||
.MapPropertyGroupBasicToPropertyGroupDisplay<PropertyGroupBasic<MemberPropertyTypeBasic>, MemberPropertyTypeBasic, MemberPropertyTypeDisplay>();
|
||||
|
||||
CreateMap<PropertyTypeBasic, PropertyTypeDisplay>()
|
||||
.ForMember(g => g.Editor, opt => opt.Ignore())
|
||||
.ForMember(g => g.View, opt => opt.Ignore())
|
||||
.ForMember(g => g.Config, opt => opt.Ignore())
|
||||
.ForMember(g => g.ContentTypeId, opt => opt.Ignore())
|
||||
.ForMember(g => g.ContentTypeName, opt => opt.Ignore())
|
||||
.ForMember(g => g.Locked, exp => exp.Ignore());
|
||||
|
||||
CreateMap<MemberPropertyTypeBasic, MemberPropertyTypeDisplay>()
|
||||
.ForMember(g => g.Editor, opt => opt.Ignore())
|
||||
.ForMember(g => g.View, opt => opt.Ignore())
|
||||
.ForMember(g => g.Config, opt => opt.Ignore())
|
||||
.ForMember(g => g.ContentTypeId, opt => opt.Ignore())
|
||||
.ForMember(g => g.ContentTypeName, opt => opt.Ignore())
|
||||
.ForMember(g => g.Locked, exp => exp.Ignore());
|
||||
|
||||
#endregion
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
289
src/Umbraco.Web/Models/Mapping/ContentTypeModelMapper.cs
Normal file
289
src/Umbraco.Web/Models/Mapping/ContentTypeModelMapper.cs
Normal file
@@ -0,0 +1,289 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using AutoMapper;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Models.Mapping;
|
||||
using Umbraco.Core.PropertyEditors;
|
||||
using Umbraco.Web.Models.ContentEditing;
|
||||
using System.Collections.Generic;
|
||||
using AutoMapper.Internal;
|
||||
using Umbraco.Core.Services;
|
||||
using Property = umbraco.NodeFactory.Property;
|
||||
|
||||
namespace Umbraco.Web.Models.Mapping
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines mappings for content/media/members type mappings
|
||||
/// </summary>
|
||||
internal class ContentTypeModelMapper : MapperConfiguration
|
||||
{
|
||||
private readonly Lazy<PropertyEditorResolver> _propertyEditorResolver;
|
||||
|
||||
//default ctor
|
||||
public ContentTypeModelMapper()
|
||||
{
|
||||
_propertyEditorResolver = new Lazy<PropertyEditorResolver>(() => PropertyEditorResolver.Current);
|
||||
}
|
||||
|
||||
//ctor can be used for testing
|
||||
public ContentTypeModelMapper(Lazy<PropertyEditorResolver> propertyEditorResolver)
|
||||
{
|
||||
_propertyEditorResolver = propertyEditorResolver;
|
||||
}
|
||||
|
||||
public override void ConfigureMappings(IConfiguration config, ApplicationContext applicationContext)
|
||||
{
|
||||
|
||||
config.CreateMap<PropertyTypeBasic, PropertyType>()
|
||||
.ConstructUsing(basic => new PropertyType(applicationContext.Services.DataTypeService.GetDataTypeDefinitionById(basic.DataTypeId)))
|
||||
.IgnoreEntityCommonProperties()
|
||||
.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.DeletedDate, expression => expression.Ignore())
|
||||
.ForMember(type => type.HasIdentity, expression => expression.Ignore());
|
||||
|
||||
config.CreateMap<DocumentTypeSave, IContentType>()
|
||||
//do the base mapping
|
||||
.MapBaseContentTypeSaveToEntity<DocumentTypeSave, PropertyTypeBasic, IContentType>(applicationContext)
|
||||
.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 => applicationContext.Services.FileService.GetTemplate(s))
|
||||
.ToArray();
|
||||
|
||||
if (source.DefaultTemplate != null)
|
||||
dest.SetDefaultTemplate(applicationContext.Services.FileService.GetTemplate(source.DefaultTemplate));
|
||||
else
|
||||
dest.SetDefaultTemplate(null);
|
||||
|
||||
ContentTypeModelMapperExtensions.AfterMapContentTypeSaveToEntity(source, dest, applicationContext);
|
||||
});
|
||||
|
||||
config.CreateMap<MediaTypeSave, IMediaType>()
|
||||
//do the base mapping
|
||||
.MapBaseContentTypeSaveToEntity<MediaTypeSave, PropertyTypeBasic, IMediaType>(applicationContext)
|
||||
.ConstructUsing((source) => new MediaType(source.ParentId))
|
||||
.AfterMap((source, dest) =>
|
||||
{
|
||||
ContentTypeModelMapperExtensions.AfterMapMediaTypeSaveToEntity(source, dest, applicationContext);
|
||||
});
|
||||
|
||||
config.CreateMap<MemberTypeSave, IMemberType>()
|
||||
//do the base mapping
|
||||
.MapBaseContentTypeSaveToEntity<MemberTypeSave, MemberPropertyTypeBasic, IMemberType>(applicationContext)
|
||||
.ConstructUsing((source) => new MemberType(source.ParentId))
|
||||
.AfterMap((source, dest) =>
|
||||
{
|
||||
ContentTypeModelMapperExtensions.AfterMapContentTypeSaveToEntity(source, dest, applicationContext);
|
||||
|
||||
//map the MemberCanEditProperty,MemberCanViewProperty,IsSensitiveData
|
||||
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);
|
||||
dest.SetIsSensitiveProperty(localCopy.Alias, localCopy.IsSensitiveData);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
config.CreateMap<IContentTypeComposition, string>().ConvertUsing(x => x.Alias);
|
||||
|
||||
config.CreateMap<IMemberType, MemberTypeDisplay>()
|
||||
//map base logic
|
||||
.MapBaseContentTypeEntityToDisplay<IMemberType, MemberTypeDisplay, MemberPropertyTypeDisplay>(applicationContext, _propertyEditorResolver)
|
||||
.AfterMap((memberType, display) =>
|
||||
{
|
||||
//map the MemberCanEditProperty,MemberCanViewProperty,IsSensitiveData
|
||||
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);
|
||||
displayProp.IsSensitiveData = memberType.IsSensitiveProperty(localCopy.Alias);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
config.CreateMap<IMediaType, MediaTypeDisplay>()
|
||||
//map base logic
|
||||
.MapBaseContentTypeEntityToDisplay<IMediaType, MediaTypeDisplay, PropertyTypeDisplay>(applicationContext, _propertyEditorResolver)
|
||||
.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 (applicationContext.Services.DataTypeService.GetDataTypeDefinitionByName(name) != null)
|
||||
dest.ListViewEditorName = name;
|
||||
}
|
||||
});
|
||||
|
||||
config.CreateMap<IContentType, DocumentTypeDisplay>()
|
||||
//map base logic
|
||||
.MapBaseContentTypeEntityToDisplay<IContentType, DocumentTypeDisplay, PropertyTypeDisplay>(applicationContext, _propertyEditorResolver)
|
||||
.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<EntityBasic>).ToArray();
|
||||
|
||||
if (source.DefaultTemplate != null)
|
||||
dest.DefaultTemplate = Mapper.Map<EntityBasic>(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 (applicationContext.Services.DataTypeService.GetDataTypeDefinitionByName(name) != null)
|
||||
dest.ListViewEditorName = name;
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
config.CreateMap<IMemberType, ContentTypeBasic>()
|
||||
.ForMember(x => x.Udi, expression => expression.MapFrom(content => Udi.Create(Constants.UdiEntityType.MemberType, content.Key)))
|
||||
.ForMember(x => x.Blueprints, expression => expression.Ignore());
|
||||
config.CreateMap<IMediaType, ContentTypeBasic>()
|
||||
.ForMember(x => x.Udi, expression => expression.MapFrom(content => Udi.Create(Constants.UdiEntityType.MediaType, content.Key)))
|
||||
.ForMember(x => x.Blueprints, expression => expression.Ignore());
|
||||
config.CreateMap<IContentType, ContentTypeBasic>()
|
||||
.ForMember(x => x.Udi, expression => expression.MapFrom(content => Udi.Create(Constants.UdiEntityType.DocumentType, content.Key)))
|
||||
.ForMember(x => x.Blueprints, expression => expression.Ignore());
|
||||
|
||||
config.CreateMap<PropertyTypeBasic, PropertyType>()
|
||||
|
||||
.ConstructUsing(propertyTypeBasic =>
|
||||
{
|
||||
var dataType = applicationContext.Services.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<int>(() => 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<MemberTypeSave, MemberTypeDisplay>()
|
||||
.MapBaseContentTypeSaveToDisplay<MemberTypeSave, MemberPropertyTypeBasic, MemberTypeDisplay, MemberPropertyTypeDisplay>();
|
||||
|
||||
config.CreateMap<MediaTypeSave, MediaTypeDisplay>()
|
||||
.MapBaseContentTypeSaveToDisplay<MediaTypeSave, PropertyTypeBasic, MediaTypeDisplay, PropertyTypeDisplay>();
|
||||
|
||||
config.CreateMap<DocumentTypeSave, DocumentTypeDisplay>()
|
||||
.MapBaseContentTypeSaveToDisplay<DocumentTypeSave, PropertyTypeBasic, DocumentTypeDisplay, PropertyTypeDisplay>()
|
||||
.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 = applicationContext.Services.FileService.GetTemplates(source.AllowedTemplates.ToArray());
|
||||
dest.AllowedTemplates = source.AllowedTemplates
|
||||
.Select(x => Mapper.Map<EntityBasic>(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 = applicationContext.Services.FileService.GetTemplate(source.DefaultTemplate);
|
||||
dest.DefaultTemplate = template == null ? null : Mapper.Map<EntityBasic>(template);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
dest.DefaultTemplate = null;
|
||||
}
|
||||
});
|
||||
|
||||
//for doc types, media types
|
||||
config.CreateMap<PropertyGroupBasic<PropertyTypeBasic>, PropertyGroup>()
|
||||
.MapPropertyGroupBasicToPropertyGroupPersistence<PropertyGroupBasic<PropertyTypeBasic>, PropertyTypeBasic>();
|
||||
|
||||
//for members
|
||||
config.CreateMap<PropertyGroupBasic<MemberPropertyTypeBasic>, PropertyGroup>()
|
||||
.MapPropertyGroupBasicToPropertyGroupPersistence<PropertyGroupBasic<MemberPropertyTypeBasic>, MemberPropertyTypeBasic>();
|
||||
|
||||
//for doc types, media types
|
||||
config.CreateMap<PropertyGroupBasic<PropertyTypeBasic>, PropertyGroupDisplay<PropertyTypeDisplay>>()
|
||||
.MapPropertyGroupBasicToPropertyGroupDisplay<PropertyGroupBasic<PropertyTypeBasic>, PropertyTypeBasic, PropertyTypeDisplay>();
|
||||
|
||||
//for members
|
||||
config.CreateMap<PropertyGroupBasic<MemberPropertyTypeBasic>, PropertyGroupDisplay<MemberPropertyTypeDisplay>>()
|
||||
.MapPropertyGroupBasicToPropertyGroupDisplay<PropertyGroupBasic<MemberPropertyTypeBasic>, MemberPropertyTypeBasic, MemberPropertyTypeDisplay>();
|
||||
|
||||
config.CreateMap<PropertyTypeBasic, PropertyTypeDisplay>()
|
||||
.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<MemberPropertyTypeBasic, MemberPropertyTypeDisplay>()
|
||||
.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
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,10 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
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;
|
||||
|
||||
namespace Umbraco.Web.Models.Mapping
|
||||
@@ -17,20 +17,20 @@ namespace Umbraco.Web.Models.Mapping
|
||||
/// to assert mappings fails which is an Automapper bug. So instead we will use an extension method for the mappings
|
||||
/// to re-use mappings.
|
||||
/// </remarks>
|
||||
internal static class ContentTypeProfileExtensions
|
||||
internal static class ContentTypeModelMapperExtensions
|
||||
{
|
||||
|
||||
public static IMappingExpression<TSource, PropertyGroup> MapPropertyGroupBasicToPropertyGroupPersistence<TSource, TPropertyTypeBasic>(
|
||||
this IMappingExpression<TSource, PropertyGroup> mapping)
|
||||
where TSource : PropertyGroupBasic<TPropertyTypeBasic>
|
||||
where TPropertyTypeBasic : PropertyTypeBasic
|
||||
{
|
||||
return mapping
|
||||
.ConstructUsing(x => new PropertyGroup(false)) // fixme - we have NO idea of isPublishing here = wtf?
|
||||
.IgnoreEntityCommonProperties()
|
||||
.ForMember(dest => dest.Id, map => map.Condition(src => src.Id > 0))
|
||||
.ForMember(dest => dest.Id, map => map.Condition(source => source.Id > 0))
|
||||
.ForMember(dest => dest.Key, map => map.Ignore())
|
||||
.ForMember(dest => dest.HasIdentity, map => map.Ignore())
|
||||
.ForMember(dest => dest.DeleteDate, map => map.Ignore())
|
||||
.ForMember(dest => dest.HasIdentity, map => map.Ignore())
|
||||
.ForMember(dest => dest.DeletedDate, map => map.Ignore())
|
||||
.ForMember(dest => dest.PropertyTypes, map => map.Ignore());
|
||||
}
|
||||
|
||||
@@ -41,14 +41,16 @@ namespace Umbraco.Web.Models.Mapping
|
||||
where TPropertyTypeDisplay : PropertyTypeDisplay
|
||||
{
|
||||
return mapping
|
||||
.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<TPropertyTypeDisplay>)));
|
||||
.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<TPropertyTypeDisplay>)));
|
||||
}
|
||||
|
||||
public static void AfterMapContentTypeSaveToEntity<TSource, TDestination>(TSource source, TDestination dest, IContentTypeService contentTypeService)
|
||||
public static void AfterMapContentTypeSaveToEntity<TSource, TDestination>(
|
||||
TSource source, TDestination dest,
|
||||
ApplicationContext applicationContext)
|
||||
where TSource : ContentTypeSave
|
||||
where TDestination : IContentTypeComposition
|
||||
{
|
||||
@@ -67,13 +69,15 @@ namespace Umbraco.Web.Models.Mapping
|
||||
foreach (var a in add)
|
||||
{
|
||||
//TODO: Remove N+1 lookup
|
||||
var addCt = contentTypeService.Get(a);
|
||||
var addCt = applicationContext.Services.ContentTypeService.GetContentType(a);
|
||||
if (addCt != null)
|
||||
dest.AddContentType(addCt);
|
||||
}
|
||||
}
|
||||
|
||||
public static void AfterMapMediaTypeSaveToEntity<TSource, TDestination>(TSource source, TDestination dest, IMediaTypeService mediaTypeService)
|
||||
public static void AfterMapMediaTypeSaveToEntity<TSource, TDestination>(
|
||||
TSource source, TDestination dest,
|
||||
ApplicationContext applicationContext)
|
||||
where TSource : MediaTypeSave
|
||||
where TDestination : IContentTypeComposition
|
||||
{
|
||||
@@ -92,7 +96,7 @@ namespace Umbraco.Web.Models.Mapping
|
||||
foreach (var a in add)
|
||||
{
|
||||
//TODO: Remove N+1 lookup
|
||||
var addCt = mediaTypeService.Get(a);
|
||||
var addCt = applicationContext.Services.ContentTypeService.GetMediaType(a);
|
||||
if (addCt != null)
|
||||
dest.AddContentType(addCt);
|
||||
}
|
||||
@@ -105,44 +109,47 @@ namespace Umbraco.Web.Models.Mapping
|
||||
where TPropertyTypeDestination : PropertyTypeDisplay
|
||||
where TPropertyTypeSource : PropertyTypeBasic
|
||||
{
|
||||
var propertyGroupDisplayResolver = new PropertyGroupDisplayResolver<TSource, TPropertyTypeSource, TPropertyTypeDestination>();
|
||||
|
||||
return mapping
|
||||
.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)));
|
||||
.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<TSource, TPropertyTypeSource, TPropertyTypeDestination>()));
|
||||
}
|
||||
|
||||
public static IMappingExpression<TSource, TDestination> MapBaseContentTypeEntityToDisplay<TSource, TDestination, TPropertyTypeDisplay>(
|
||||
this IMappingExpression<TSource, TDestination> mapping, PropertyEditorCollection propertyEditors,
|
||||
IDataTypeService dataTypeService, IContentTypeService contentTypeService)
|
||||
this IMappingExpression<TSource, TDestination> mapping, ApplicationContext applicationContext, Lazy<PropertyEditorResolver> propertyEditorResolver)
|
||||
where TSource : IContentTypeComposition
|
||||
where TDestination : ContentTypeCompositionDisplay<TPropertyTypeDisplay>
|
||||
where TPropertyTypeDisplay : PropertyTypeDisplay, new()
|
||||
{
|
||||
var contentTypeUdiResolver = new ContentTypeUdiResolver();
|
||||
var lockedCompositionsResolver = new LockedCompositionsResolver(contentTypeService);
|
||||
var propertyTypeGroupResolver = new PropertyTypeGroupResolver<TPropertyTypeDisplay>(propertyEditors, dataTypeService);
|
||||
|
||||
return mapping
|
||||
.ForMember(dest => dest.Udi, opt => opt.ResolveUsing(src => contentTypeUdiResolver.Resolve(src)))
|
||||
.ForMember(dest => dest.Notifications, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.Blueprints, 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())
|
||||
.ForMember(x => x.Udi, expression => expression.ResolveUsing(new ContentTypeUdiResolver()))
|
||||
.ForMember(display => display.Notifications, expression => expression.Ignore())
|
||||
.ForMember(display => display.Blueprints, 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())
|
||||
//Ignore because this is not actually used for content types
|
||||
.ForMember(dest => dest.Trashed, opt => opt.Ignore())
|
||||
.ForMember(display => display.Trashed, expression => expression.Ignore())
|
||||
|
||||
.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)))
|
||||
.ForMember(dest => dest.AdditionalData, 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(applicationContext)))
|
||||
|
||||
.ForMember(
|
||||
dto => dto.Groups,
|
||||
expression => expression.ResolveUsing(new PropertyTypeGroupResolver<TPropertyTypeDisplay>(applicationContext, propertyEditorResolver)));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -152,46 +159,38 @@ namespace Umbraco.Web.Models.Mapping
|
||||
/// <typeparam name="TDestination"></typeparam>
|
||||
/// <typeparam name="TSourcePropertyType"></typeparam>
|
||||
/// <param name="mapping"></param>
|
||||
/// <returns></returns>
|
||||
/// <param name="applicationContext"></param>
|
||||
/// <returns></returns>
|
||||
public static IMappingExpression<TSource, TDestination> MapBaseContentTypeSaveToEntity<TSource, TSourcePropertyType, TDestination>(
|
||||
this IMappingExpression<TSource, TDestination> mapping)
|
||||
this IMappingExpression<TSource, TDestination> mapping, ApplicationContext applicationContext)
|
||||
//where TSource : ContentTypeCompositionDisplay
|
||||
where TSource : ContentTypeSave<TSourcePropertyType>
|
||||
where TDestination : IContentTypeComposition
|
||||
where TSourcePropertyType : PropertyTypeBasic
|
||||
{
|
||||
// fixme not so clean really
|
||||
var isPublishing = typeof(IContentType).IsAssignableFrom(typeof(TDestination));
|
||||
|
||||
mapping = mapping
|
||||
return mapping
|
||||
//only map id if set to something higher then zero
|
||||
.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)))
|
||||
.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)))
|
||||
|
||||
//These get persisted as part of the saving procedure, nothing to do with the display model
|
||||
.IgnoreEntityCommonProperties()
|
||||
.IgnoreDeletableEntityCommonProperties()
|
||||
|
||||
.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())
|
||||
.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())
|
||||
//ignore, we'll do this in after map
|
||||
.ForMember(dest => dest.PropertyGroups, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.NoGroupPropertyTypes, opt => opt.Ignore())
|
||||
.ForMember(dto => dto.PropertyGroups, expression => expression.Ignore())
|
||||
.ForMember(dto => dto.NoGroupPropertyTypes, expression => expression.Ignore())
|
||||
// ignore, composition is managed in AfterMapContentTypeSaveToEntity
|
||||
.ForMember(dest => dest.ContentTypeComposition, opt => opt.Ignore());
|
||||
.ForMember(dest => dest.ContentTypeComposition, opt => opt.Ignore())
|
||||
|
||||
// ignore for members
|
||||
mapping = typeof(TDestination) == typeof(IMemberType)
|
||||
? mapping.ForMember(dto => dto.Variations, opt => opt.Ignore())
|
||||
: mapping.ForMember(dto => dto.Variations, opt => opt.ResolveUsing<ContentTypeVariationsResolver<TSource, TSourcePropertyType, TDestination>>());
|
||||
|
||||
mapping = mapping
|
||||
.ForMember(
|
||||
dest => dest.AllowedContentTypes,
|
||||
opt => opt.MapFrom(src => src.AllowedContentTypes.Select((t, i) => new ContentTypeSort(t, i))))
|
||||
dto => dto.AllowedContentTypes,
|
||||
expression => expression.MapFrom(dto => dto.AllowedContentTypes.Select((t, i) => new ContentTypeSort(t, i))))
|
||||
|
||||
.AfterMap((src, dest) =>
|
||||
.AfterMap((source, dest) =>
|
||||
{
|
||||
// handle property groups and property types
|
||||
// note that ContentTypeSave has
|
||||
@@ -213,7 +212,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<PropertyGroup>();
|
||||
var sourceGroups = src.Groups.Where(x => x.IsGenericProperties == false).ToArray();
|
||||
var sourceGroups = source.Groups.Where(x => x.IsGenericProperties == false).ToArray();
|
||||
foreach (var sourceGroup in sourceGroups)
|
||||
{
|
||||
// get the dest group
|
||||
@@ -232,7 +231,7 @@ namespace Umbraco.Web.Models.Mapping
|
||||
|
||||
// ensure no duplicate alias, then assign the group properties collection
|
||||
EnsureUniqueAliases(destProperties);
|
||||
destGroup.PropertyTypes = new PropertyTypeCollection(isPublishing, destProperties);
|
||||
destGroup.PropertyTypes = new PropertyTypeCollection(destProperties);
|
||||
destGroups.Add(destGroup);
|
||||
}
|
||||
|
||||
@@ -244,7 +243,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 = src.Groups.FirstOrDefault(x => x.IsGenericProperties);
|
||||
var genericPropertiesGroup = source.Groups.FirstOrDefault(x => x.IsGenericProperties);
|
||||
if (genericPropertiesGroup != null)
|
||||
{
|
||||
// handle local properties
|
||||
@@ -255,14 +254,12 @@ namespace Umbraco.Web.Models.Mapping
|
||||
|
||||
// ensure no duplicate alias, then assign the generic properties collection
|
||||
EnsureUniqueAliases(destProperties);
|
||||
dest.NoGroupPropertyTypes = new PropertyTypeCollection(isPublishing, destProperties);
|
||||
dest.NoGroupPropertyTypes = new PropertyTypeCollection(destProperties);
|
||||
}
|
||||
|
||||
// because all property collections were rebuilt, there is no need to remove
|
||||
// some old properties, they are just gone and will be cleared by the repository
|
||||
});
|
||||
|
||||
return mapping;
|
||||
}
|
||||
|
||||
private static PropertyGroup MapSaveGroup<TPropertyType>(PropertyGroupBasic<TPropertyType> sourceGroup, IEnumerable<PropertyGroup> destOrigGroups)
|
||||
@@ -309,7 +306,7 @@ namespace Umbraco.Web.Models.Mapping
|
||||
sourceProperty.Id = 0;
|
||||
}
|
||||
|
||||
// insert a new property, or update an existing property that has
|
||||
// insert a new property, or update an existing property that has
|
||||
// been deletedin the meantime and we need to re-create
|
||||
// map/create
|
||||
destProperty = Mapper.Map<PropertyType>(sourceProperty);
|
||||
@@ -338,4 +335,4 @@ namespace Umbraco.Web.Models.Mapping
|
||||
throw new InvalidOperationException("Cannot map groups due to name conflict.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using Umbraco.Core;
|
||||
using AutoMapper;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Models;
|
||||
|
||||
namespace Umbraco.Web.Models.Mapping
|
||||
@@ -6,9 +7,9 @@ namespace Umbraco.Web.Models.Mapping
|
||||
/// <summary>
|
||||
/// Resolves a UDI for a content type based on it's type
|
||||
/// </summary>
|
||||
internal class ContentTypeUdiResolver
|
||||
internal class ContentTypeUdiResolver : ValueResolver<IContentTypeComposition, Udi>
|
||||
{
|
||||
public Udi Resolve(IContentTypeComposition source)
|
||||
protected override Udi ResolveCore(IContentTypeComposition source)
|
||||
{
|
||||
if (source == null) return null;
|
||||
|
||||
@@ -19,4 +20,4 @@ namespace Umbraco.Web.Models.Mapping
|
||||
? Constants.UdiEntityType.MediaType : Constants.UdiEntityType.DocumentType, source.Key);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
using AutoMapper;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Web.Models.ContentEditing;
|
||||
using ContentVariation = Umbraco.Core.Models.ContentVariation;
|
||||
|
||||
namespace Umbraco.Web.Models.Mapping
|
||||
{
|
||||
internal class ContentTypeVariationsResolver<TSource, TSourcePropertyType, TDestination> : IValueResolver<TSource, TDestination, ContentVariation>
|
||||
where TSource : ContentTypeSave<TSourcePropertyType>
|
||||
where TDestination : IContentTypeComposition
|
||||
where TSourcePropertyType : PropertyTypeBasic
|
||||
{
|
||||
public ContentVariation Resolve(TSource source, TDestination destination, ContentVariation destMember, ResolutionContext context)
|
||||
{
|
||||
//this will always be the case, a content type will always be allowed to be invariant
|
||||
var result = ContentVariation.Nothing;
|
||||
|
||||
if (source.AllowCultureVariant)
|
||||
result |= ContentVariation.Culture;
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
using System.Linq;
|
||||
using AutoMapper;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Services;
|
||||
using Umbraco.Web.Models.ContentEditing;
|
||||
using Umbraco.Web.Routing;
|
||||
|
||||
namespace Umbraco.Web.Models.Mapping
|
||||
{
|
||||
internal class ContentUrlResolver : IValueResolver<IContent, ContentItemDisplay, UrlInfo[]>
|
||||
{
|
||||
private readonly IUmbracoContextAccessor _umbracoContextAccessor;
|
||||
private readonly PublishedRouter _publishedRouter;
|
||||
private readonly ILocalizationService _localizationService;
|
||||
private readonly ILocalizedTextService _textService;
|
||||
private readonly IContentService _contentService;
|
||||
private readonly ILogger _logger;
|
||||
|
||||
public ContentUrlResolver(
|
||||
IUmbracoContextAccessor umbracoContextAccessor,
|
||||
PublishedRouter publishedRouter,
|
||||
ILocalizationService localizationService,
|
||||
ILocalizedTextService textService,
|
||||
IContentService contentService,
|
||||
ILogger logger)
|
||||
{
|
||||
_umbracoContextAccessor = umbracoContextAccessor ?? throw new System.ArgumentNullException(nameof(umbracoContextAccessor));
|
||||
_publishedRouter = publishedRouter ?? throw new System.ArgumentNullException(nameof(publishedRouter));
|
||||
_localizationService = localizationService ?? throw new System.ArgumentNullException(nameof(localizationService));
|
||||
_textService = textService ?? throw new System.ArgumentNullException(nameof(textService));
|
||||
_contentService = contentService ?? throw new System.ArgumentNullException(nameof(contentService));
|
||||
_logger = logger ?? throw new System.ArgumentNullException(nameof(logger));
|
||||
}
|
||||
|
||||
public UrlInfo[] Resolve(IContent source, ContentItemDisplay destination, UrlInfo[] destMember, ResolutionContext context)
|
||||
{
|
||||
var umbracoContext = _umbracoContextAccessor.UmbracoContext;
|
||||
|
||||
var urls = umbracoContext == null
|
||||
? new[] { UrlInfo.Message("Cannot generate urls without a current Umbraco Context") }
|
||||
: source.GetContentUrls(_publishedRouter, umbracoContext, _localizationService, _textService, _contentService, _logger).ToArray();
|
||||
|
||||
return urls;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,27 +1,19 @@
|
||||
using AutoMapper;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Models.Membership;
|
||||
using Umbraco.Core.Services;
|
||||
using UserProfile = Umbraco.Web.Models.ContentEditing.UserProfile;
|
||||
|
||||
namespace Umbraco.Web.Models.Mapping
|
||||
{
|
||||
/// <summary>
|
||||
/// Maps the Creator for content
|
||||
/// </summary>
|
||||
internal class CreatorResolver
|
||||
{
|
||||
private readonly IUserService _userService;
|
||||
|
||||
public CreatorResolver(IUserService userService)
|
||||
{
|
||||
_userService = userService;
|
||||
}
|
||||
|
||||
public UserProfile Resolve(IContent source)
|
||||
{
|
||||
return Mapper.Map<IProfile, UserProfile>(source.GetWriterProfile(_userService));
|
||||
}
|
||||
}
|
||||
}
|
||||
using AutoMapper;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Models.Membership;
|
||||
using Umbraco.Web.Models.ContentEditing;
|
||||
using UserProfile = Umbraco.Web.Models.ContentEditing.UserProfile;
|
||||
|
||||
namespace Umbraco.Web.Models.Mapping
|
||||
{
|
||||
/// <summary>
|
||||
/// Maps the Creator for content
|
||||
/// </summary>
|
||||
internal class CreatorResolver : ValueResolver<IContent, UserProfile>
|
||||
{
|
||||
protected override UserProfile ResolveCore(IContent source)
|
||||
{
|
||||
return Mapper.Map<IProfile, UserProfile>(source.GetWriterProfile());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using AutoMapper;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Web.Composing;
|
||||
using Umbraco.Web.Models.ContentEditing;
|
||||
|
||||
namespace Umbraco.Web.Models.Mapping
|
||||
{
|
||||
internal class DataTypeConfigurationFieldDisplayResolver
|
||||
{
|
||||
private readonly ILogger _logger;
|
||||
|
||||
public DataTypeConfigurationFieldDisplayResolver(ILogger logger)
|
||||
{
|
||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Maps pre-values in the dictionary to the values for the fields
|
||||
/// </summary>
|
||||
internal static void MapConfigurationFields(ILogger logger, DataTypeConfigurationFieldDisplay[] fields, IDictionary<string, object> configuration)
|
||||
{
|
||||
if (fields == null) throw new ArgumentNullException(nameof(fields));
|
||||
if (configuration == null) throw new ArgumentNullException(nameof(configuration));
|
||||
|
||||
// now we need to wire up the pre-values values with the actual fields defined
|
||||
foreach (var field in fields)
|
||||
{
|
||||
if (configuration.TryGetValue(field.Key, out var value))
|
||||
field.Value = value;
|
||||
else
|
||||
{
|
||||
// weird - just leave the field without a value - but warn
|
||||
logger.Warn<DataTypeConfigurationFieldDisplayResolver>("Could not find a value for configuration field '{ConfigField}'", field.Key);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a set of configuration fields for a data type.
|
||||
/// </summary>
|
||||
public IEnumerable<DataTypeConfigurationFieldDisplay> Resolve(IDataType dataType)
|
||||
{
|
||||
// in v7 it was apparently fine to have an empty .EditorAlias here, in which case we would map onto
|
||||
// an empty fields list, which made no sense since there would be nothing to map to - and besides,
|
||||
// a datatype without an editor alias is a serious issue - v8 wants an editor here
|
||||
|
||||
if (string.IsNullOrWhiteSpace(dataType.EditorAlias) || !Current.PropertyEditors.TryGet(dataType.EditorAlias, out var editor))
|
||||
throw new InvalidOperationException($"Could not find a property editor with alias \"{dataType.EditorAlias}\".");
|
||||
|
||||
var configurationEditor = editor.GetConfigurationEditor();
|
||||
var fields = configurationEditor.Fields.Select(Mapper.Map<DataTypeConfigurationFieldDisplay>).ToArray();
|
||||
var configurationDictionary = configurationEditor.ToConfigurationEditor(dataType.Configuration);
|
||||
|
||||
MapConfigurationFields(_logger, fields, configurationDictionary);
|
||||
|
||||
return fields;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,129 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using AutoMapper;
|
||||
using System.Linq;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Configuration;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.PropertyEditors;
|
||||
using Umbraco.Web.Composing;
|
||||
using Umbraco.Web.Models.ContentEditing;
|
||||
|
||||
namespace Umbraco.Web.Models.Mapping
|
||||
{
|
||||
/// <summary>
|
||||
/// Configures model mappings for datatypes.
|
||||
/// </summary>
|
||||
internal class DataTypeMapperProfile : Profile
|
||||
{
|
||||
public DataTypeMapperProfile(PropertyEditorCollection propertyEditors, ILogger logger)
|
||||
{
|
||||
// create, capture, cache
|
||||
var availablePropertyEditorsResolver = new AvailablePropertyEditorsResolver(UmbracoConfig.For.UmbracoSettings().Content);
|
||||
var configurationDisplayResolver = new DataTypeConfigurationFieldDisplayResolver(logger);
|
||||
var databaseTypeResolver = new DatabaseTypeResolver();
|
||||
|
||||
CreateMap<IDataEditor, PropertyEditorBasic>();
|
||||
|
||||
// map the standard properties, not the values
|
||||
CreateMap<ConfigurationField, DataTypeConfigurationFieldDisplay>()
|
||||
.ForMember(dest => dest.Value, opt => opt.Ignore());
|
||||
|
||||
var systemIds = new[]
|
||||
{
|
||||
Constants.DataTypes.DefaultContentListView,
|
||||
Constants.DataTypes.DefaultMediaListView,
|
||||
Constants.DataTypes.DefaultMembersListView
|
||||
};
|
||||
|
||||
CreateMap<IDataEditor, DataTypeBasic>()
|
||||
.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<IDataType, DataTypeBasic>()
|
||||
.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)))
|
||||
.ForMember(dest => dest.AdditionalData, opt => opt.Ignore())
|
||||
.AfterMap((src, dest) =>
|
||||
{
|
||||
if (Current.PropertyEditors.TryGet(src.EditorAlias, out var editor))
|
||||
{
|
||||
dest.Alias = editor.Alias;
|
||||
dest.Group = editor.Group;
|
||||
dest.Icon = editor.Icon;
|
||||
}
|
||||
});
|
||||
|
||||
CreateMap<IDataType, DataTypeDisplay>()
|
||||
.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 => configurationDisplayResolver.Resolve(src)))
|
||||
.ForMember(dest => dest.SelectedEditor, opt => opt.MapFrom(src => src.EditorAlias.IsNullOrWhiteSpace() ? null : src.EditorAlias))
|
||||
.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)))
|
||||
.ForMember(dest => dest.AdditionalData, opt => opt.Ignore())
|
||||
.AfterMap((src, dest) =>
|
||||
{
|
||||
if (Current.PropertyEditors.TryGet(src.EditorAlias, out var editor))
|
||||
{
|
||||
dest.Group = editor.Group;
|
||||
dest.Icon = editor.Icon;
|
||||
}
|
||||
});
|
||||
|
||||
//gets a list of PreValueFieldDisplay objects from the data type definition
|
||||
CreateMap<IDataType, IEnumerable<DataTypeConfigurationFieldDisplay>>()
|
||||
.ConvertUsing(src => configurationDisplayResolver.Resolve(src));
|
||||
|
||||
CreateMap<DataTypeSave, IDataType>()
|
||||
.ConstructUsing(src => new DataType(propertyEditors[src.EditorAlias]) {CreateDate = DateTime.Now})
|
||||
.IgnoreEntityCommonProperties()
|
||||
.ForMember(dest => dest.Id, opt => opt.MapFrom(src => Convert.ToInt32(src.Id)))
|
||||
.ForMember(dest => dest.Key, opt => opt.Ignore()) // ignore key, else resets UniqueId - U4-3911
|
||||
.ForMember(dest => dest.Path, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.EditorAlias, opt => opt.MapFrom(src => src.EditorAlias))
|
||||
.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.Configuration, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.Editor, opt => opt.MapFrom(src => propertyEditors[src.EditorAlias]));
|
||||
|
||||
//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<IDataEditor, IEnumerable<DataTypeConfigurationFieldDisplay>>()
|
||||
.ConvertUsing(src =>
|
||||
{
|
||||
// this is a new data type, initialize default configuration
|
||||
// get the configuration editor,
|
||||
// get the configuration fields and map to UI,
|
||||
// get the configuration default values and map to UI
|
||||
|
||||
var configurationEditor = src.GetConfigurationEditor();
|
||||
|
||||
var fields = configurationEditor.Fields.Select(Mapper.Map<DataTypeConfigurationFieldDisplay>).ToArray();
|
||||
|
||||
var defaultConfiguration = configurationEditor.DefaultConfiguration;
|
||||
if (defaultConfiguration != null)
|
||||
DataTypeConfigurationFieldDisplayResolver.MapConfigurationFields(logger, fields, defaultConfiguration);
|
||||
|
||||
return fields;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
125
src/Umbraco.Web/Models/Mapping/DataTypeModelMapper.cs
Normal file
125
src/Umbraco.Web/Models/Mapping/DataTypeModelMapper.cs
Normal file
@@ -0,0 +1,125 @@
|
||||
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.Models.ContentEditing;
|
||||
|
||||
namespace Umbraco.Web.Models.Mapping
|
||||
{
|
||||
/// <summary>
|
||||
/// Configure's model mappings for Data types
|
||||
/// </summary>
|
||||
internal class DataTypeModelMapper : MapperConfiguration
|
||||
{
|
||||
public override void ConfigureMappings(IConfiguration config, ApplicationContext applicationContext)
|
||||
{
|
||||
config.CreateMap<PropertyEditor, PropertyEditorBasic>();
|
||||
|
||||
//just maps the standard properties, does not map the value!
|
||||
config.CreateMap<PreValueField, PreValueFieldDisplay>()
|
||||
.ForMember(x => x.Value, expression => expression.Ignore());
|
||||
|
||||
var systemIds = new[]
|
||||
{
|
||||
Constants.System.DefaultContentListViewDataTypeId,
|
||||
Constants.System.DefaultMediaListViewDataTypeId,
|
||||
Constants.System.DefaultMembersListViewDataTypeId
|
||||
};
|
||||
|
||||
config.CreateMap<PropertyEditor, DataTypeBasic>()
|
||||
.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<IDataTypeDefinition, DataTypeBasic>()
|
||||
.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 = PropertyEditorResolver.Current.GetByAlias(def.PropertyEditorAlias);
|
||||
if (editor != null)
|
||||
{
|
||||
basic.Alias = editor.Alias;
|
||||
basic.Group = editor.Group;
|
||||
basic.Icon = editor.Icon;
|
||||
}
|
||||
});
|
||||
|
||||
config.CreateMap<IDataTypeDefinition, DataTypeDisplay>()
|
||||
.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(applicationContext.Services.DataTypeService)))
|
||||
.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 = PropertyEditorResolver.Current.GetByAlias(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<IDataTypeDefinition, IEnumerable<PreValueFieldDisplay>>()
|
||||
.ConvertUsing(definition =>
|
||||
{
|
||||
var resolver = new PreValueDisplayResolver(applicationContext.Services.DataTypeService);
|
||||
return resolver.Convert(definition);
|
||||
});
|
||||
|
||||
config.CreateMap<DataTypeSave, IDataTypeDefinition>()
|
||||
.ConstructUsing(save => new DataTypeDefinition(save.SelectedEditor) {CreateDate = DateTime.Now})
|
||||
.IgnoreDeletableEntityCommonProperties()
|
||||
.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.ControlId, expression => expression.Ignore())
|
||||
.ForMember(x => x.CreatorId, expression => expression.Ignore())
|
||||
.ForMember(x => x.Level, expression => expression.Ignore())
|
||||
.ForMember(x => x.SortOrder, 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<PropertyEditor, IEnumerable<PreValueFieldDisplay>>()
|
||||
.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<PreValueFieldDisplay>).ToArray();
|
||||
if (defaultVals != null)
|
||||
{
|
||||
PreValueDisplayResolver.MapPreValueValuesToPreValueFields(fields, defaultVals, editor.Alias);
|
||||
}
|
||||
return fields;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,24 +1,24 @@
|
||||
using System;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.PropertyEditors;
|
||||
using Umbraco.Web.Composing;
|
||||
using Umbraco.Web.Models.ContentEditing;
|
||||
|
||||
namespace Umbraco.Web.Models.Mapping
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the DataTypeDatabaseType from the selected property editor for the data type
|
||||
/// </summary>
|
||||
internal class DatabaseTypeResolver
|
||||
{
|
||||
public ValueStorageType Resolve(DataTypeSave source)
|
||||
{
|
||||
if (!Current.PropertyEditors.TryGet(source.EditorAlias, out var editor))
|
||||
throw new InvalidOperationException($"Could not find property editor \"{source.EditorAlias}\".");
|
||||
|
||||
// fixme - what about source.PropertyEditor? can we get the configuration here? 'cos it may change the storage type?!
|
||||
var valueType = editor.GetValueEditor().ValueType;
|
||||
return ValueTypes.ToStorageType(valueType);
|
||||
}
|
||||
}
|
||||
}
|
||||
using System;
|
||||
using AutoMapper;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.PropertyEditors;
|
||||
using Umbraco.Web.Models.ContentEditing;
|
||||
|
||||
namespace Umbraco.Web.Models.Mapping
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the DataTypeDatabaseType from the selected property editor for the data type
|
||||
/// </summary>
|
||||
internal class DatabaseTypeResolver : ValueResolver<DataTypeSave, DataTypeDatabaseType>
|
||||
{
|
||||
protected override DataTypeDatabaseType ResolveCore(DataTypeSave source)
|
||||
{
|
||||
var propertyEditor = PropertyEditorResolver.Current.GetByAlias(source.SelectedEditor);
|
||||
if (propertyEditor == null)
|
||||
{
|
||||
throw new InvalidOperationException("Could not find property editor with id " + source.SelectedEditor);
|
||||
}
|
||||
return propertyEditor.ValueEditor.GetDatabaseType();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
using AutoMapper;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Web.Models.ContentEditing;
|
||||
|
||||
namespace Umbraco.Web.Models.Mapping
|
||||
{
|
||||
internal class DefaultTemplateResolver : IValueResolver<IContent, ContentItemDisplay, string>
|
||||
{
|
||||
public string Resolve(IContent source, ContentItemDisplay destination, string destMember, ResolutionContext context)
|
||||
{
|
||||
if (source == null || source.Template == null) return null;
|
||||
|
||||
var alias = source.Template.Alias;
|
||||
|
||||
//set default template if template isn't set
|
||||
if (string.IsNullOrEmpty(alias))
|
||||
alias = source.ContentType.DefaultTemplate == null
|
||||
? string.Empty
|
||||
: source.ContentType.DefaultTemplate.Alias;
|
||||
|
||||
return alias;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,127 +0,0 @@
|
||||
using AutoMapper;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Services;
|
||||
using Umbraco.Web.Models.ContentEditing;
|
||||
|
||||
namespace Umbraco.Web.Models.Mapping
|
||||
{
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// The dictionary model mapper.
|
||||
/// </summary>
|
||||
internal class DictionaryMapperProfile : Profile
|
||||
{
|
||||
public DictionaryMapperProfile(ILocalizationService localizationService)
|
||||
{
|
||||
CreateMap<IDictionaryItem, DictionaryDisplay>()
|
||||
.ForMember(x => x.Translations, expression => expression.Ignore())
|
||||
.ForMember(x => x.Notifications, expression => expression.Ignore())
|
||||
.ForMember(x => x.Icon, expression => expression.Ignore())
|
||||
.ForMember(x => x.Trashed, expression => expression.Ignore())
|
||||
.ForMember(x => x.Alias, expression => expression.Ignore())
|
||||
.ForMember(x => x.Path, expression => expression.Ignore())
|
||||
.ForMember(x => x.AdditionalData, expression => expression.Ignore())
|
||||
.ForMember(
|
||||
x => x.Udi,
|
||||
expression => expression.MapFrom(
|
||||
content => Udi.Create(Constants.UdiEntityType.DictionaryItem, content.Key))).ForMember(
|
||||
x => x.Name,
|
||||
expression => expression.MapFrom(content => content.ItemKey))
|
||||
.AfterMap(
|
||||
(src, dest) =>
|
||||
{
|
||||
// build up the path to make it possible to set active item in tree
|
||||
// TODO check if there is a better way
|
||||
if (src.ParentId.HasValue)
|
||||
{
|
||||
var ids = new List<int> { -1 };
|
||||
|
||||
|
||||
var parentIds = new List<int>();
|
||||
|
||||
this.GetParentId(src.ParentId.Value, localizationService, parentIds);
|
||||
|
||||
parentIds.Reverse();
|
||||
|
||||
ids.AddRange(parentIds);
|
||||
|
||||
ids.Add(src.Id);
|
||||
|
||||
dest.Path = string.Join(",", ids);
|
||||
}
|
||||
else
|
||||
{
|
||||
dest.Path = "-1," + src.Id;
|
||||
}
|
||||
|
||||
// add all languages and the translations
|
||||
foreach (var lang in localizationService.GetAllLanguages())
|
||||
{
|
||||
var langId = lang.Id;
|
||||
var translation = src.Translations.FirstOrDefault(x => x.LanguageId == langId);
|
||||
|
||||
dest.Translations.Add(new DictionaryTranslationDisplay
|
||||
{
|
||||
IsoCode = lang.IsoCode,
|
||||
DisplayName = lang.CultureInfo.DisplayName,
|
||||
Translation = (translation != null) ? translation.Value : string.Empty,
|
||||
LanguageId = lang.Id
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
CreateMap<IDictionaryItem, DictionaryOverviewDisplay>()
|
||||
.ForMember(dest => dest.Level, expression => expression.Ignore())
|
||||
.ForMember(dest => dest.Translations, expression => expression.Ignore())
|
||||
.ForMember(
|
||||
x => x.Name,
|
||||
expression => expression.MapFrom(content => content.ItemKey))
|
||||
.AfterMap(
|
||||
(src, dest) =>
|
||||
{
|
||||
// add all languages and the translations
|
||||
foreach (var lang in localizationService.GetAllLanguages())
|
||||
{
|
||||
var langId = lang.Id;
|
||||
var translation = src.Translations.FirstOrDefault(x => x.LanguageId == langId);
|
||||
|
||||
dest.Translations.Add(
|
||||
new DictionaryOverviewTranslationDisplay
|
||||
{
|
||||
DisplayName = lang.CultureInfo.DisplayName,
|
||||
HasTranslation = translation != null && string.IsNullOrEmpty(translation.Value) == false
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Goes up the dictoinary tree to get all parent ids
|
||||
/// </summary>
|
||||
/// <param name="parentId">
|
||||
/// The parent id.
|
||||
/// </param>
|
||||
/// <param name="localizationService">
|
||||
/// The localization service.
|
||||
/// </param>
|
||||
/// <param name="ids">
|
||||
/// The ids.
|
||||
/// </param>
|
||||
private void GetParentId(Guid parentId, ILocalizationService localizationService, List<int> ids)
|
||||
{
|
||||
var dictionary = localizationService.GetDictionaryItemById(parentId);
|
||||
|
||||
if (dictionary == null)
|
||||
return;
|
||||
|
||||
ids.Add(dictionary.Id);
|
||||
|
||||
if (dictionary.ParentId.HasValue)
|
||||
GetParentId(dictionary.ParentId.Value, localizationService, ids);
|
||||
}
|
||||
}
|
||||
}
|
||||
130
src/Umbraco.Web/Models/Mapping/DictionaryModelMapper.cs
Normal file
130
src/Umbraco.Web/Models/Mapping/DictionaryModelMapper.cs
Normal file
@@ -0,0 +1,130 @@
|
||||
using AutoMapper;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Models.Mapping;
|
||||
using Umbraco.Core.Services;
|
||||
using Umbraco.Web.Models.ContentEditing;
|
||||
|
||||
namespace Umbraco.Web.Models.Mapping
|
||||
{
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// The dictionary model mapper.
|
||||
/// </summary>
|
||||
internal class DictionaryModelMapper : MapperConfiguration
|
||||
{
|
||||
public override void ConfigureMappings(IConfiguration config, ApplicationContext applicationContext)
|
||||
{
|
||||
var lazyDictionaryService = new Lazy<ILocalizationService>(() => applicationContext.Services.LocalizationService);
|
||||
|
||||
config.CreateMap<IDictionaryItem, DictionaryDisplay>()
|
||||
.ForMember(x => x.Translations, expression => expression.Ignore())
|
||||
.ForMember(x => x.Notifications, expression => expression.Ignore())
|
||||
.ForMember(x => x.Icon, expression => expression.Ignore())
|
||||
.ForMember(x => x.Trashed, expression => expression.Ignore())
|
||||
.ForMember(x => x.Alias, expression => expression.Ignore())
|
||||
.ForMember(x => x.Path, expression => expression.Ignore())
|
||||
.ForMember(x => x.AdditionalData, expression => expression.Ignore())
|
||||
.ForMember(
|
||||
x => x.Udi,
|
||||
expression => expression.MapFrom(
|
||||
content => Udi.Create(Constants.UdiEntityType.DictionaryItem, content.Key))).ForMember(
|
||||
x => x.Name,
|
||||
expression => expression.MapFrom(content => content.ItemKey))
|
||||
.AfterMap(
|
||||
(src, dest) =>
|
||||
{
|
||||
// build up the path to make it possible to set active item in tree
|
||||
// TODO check if there is a better way
|
||||
if (src.ParentId.HasValue)
|
||||
{
|
||||
var ids = new List<int> { -1 };
|
||||
|
||||
|
||||
var parentIds = new List<int>();
|
||||
|
||||
this.GetParentId(src.ParentId.Value, lazyDictionaryService.Value, parentIds);
|
||||
|
||||
parentIds.Reverse();
|
||||
|
||||
ids.AddRange(parentIds);
|
||||
|
||||
ids.Add(src.Id);
|
||||
|
||||
dest.Path = string.Join(",", ids);
|
||||
}
|
||||
else
|
||||
{
|
||||
dest.Path = "-1," + src.Id;
|
||||
}
|
||||
|
||||
// add all languages and the translations
|
||||
foreach (var lang in lazyDictionaryService.Value.GetAllLanguages())
|
||||
{
|
||||
var langId = lang.Id;
|
||||
var translation = src.Translations.FirstOrDefault(x => x.LanguageId == langId);
|
||||
|
||||
dest.Translations.Add(new DictionaryTranslationDisplay
|
||||
{
|
||||
IsoCode = lang.IsoCode,
|
||||
DisplayName = lang.CultureInfo.DisplayName,
|
||||
Translation = (translation != null) ? translation.Value : string.Empty,
|
||||
LanguageId = lang.Id
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
config.CreateMap<IDictionaryItem, DictionaryOverviewDisplay>()
|
||||
.ForMember(dest => dest.Level, expression => expression.Ignore())
|
||||
.ForMember(dest => dest.Translations, expression => expression.Ignore())
|
||||
.ForMember(
|
||||
x => x.Name,
|
||||
expression => expression.MapFrom(content => content.ItemKey))
|
||||
.AfterMap(
|
||||
(src, dest) =>
|
||||
{
|
||||
// add all languages and the translations
|
||||
foreach (var lang in lazyDictionaryService.Value.GetAllLanguages())
|
||||
{
|
||||
var langId = lang.Id;
|
||||
var translation = src.Translations.FirstOrDefault(x => x.LanguageId == langId);
|
||||
|
||||
dest.Translations.Add(
|
||||
new DictionaryOverviewTranslationDisplay
|
||||
{
|
||||
DisplayName = lang.CultureInfo.DisplayName,
|
||||
HasTranslation = translation != null && string.IsNullOrEmpty(translation.Value) == false
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Goes up the dictoinary tree to get all parent ids
|
||||
/// </summary>
|
||||
/// <param name="parentId">
|
||||
/// The parent id.
|
||||
/// </param>
|
||||
/// <param name="localizationService">
|
||||
/// The localization service.
|
||||
/// </param>
|
||||
/// <param name="ids">
|
||||
/// The ids.
|
||||
/// </param>
|
||||
private void GetParentId(Guid parentId, ILocalizationService localizationService, List<int> ids)
|
||||
{
|
||||
var dictionary = localizationService.GetDictionaryItemById(parentId);
|
||||
|
||||
if (dictionary == null)
|
||||
return;
|
||||
|
||||
ids.Add(dictionary.Id);
|
||||
|
||||
if (dictionary.ParentId.HasValue)
|
||||
GetParentId(dictionary.ParentId.Value, localizationService, ids);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,212 +0,0 @@
|
||||
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.Entities;
|
||||
using Umbraco.Core.Models.Membership;
|
||||
using Umbraco.Web.Models.ContentEditing;
|
||||
using Umbraco.Examine;
|
||||
|
||||
namespace Umbraco.Web.Models.Mapping
|
||||
{
|
||||
internal class EntityMapperProfile : Profile
|
||||
{
|
||||
private static string GetContentTypeIcon(EntitySlim entity)
|
||||
=> entity is ContentEntitySlim contentEntity ? contentEntity.ContentTypeIcon : null;
|
||||
|
||||
public EntityMapperProfile()
|
||||
{
|
||||
// create, capture, cache
|
||||
var contentTypeUdiResolver = new ContentTypeUdiResolver();
|
||||
|
||||
CreateMap<EntitySlim, EntityBasic>()
|
||||
.ForMember(dest => dest.Name, opt => opt.ResolveUsing<NameResolver>())
|
||||
.ForMember(dest => dest.Udi, opt => opt.MapFrom(src => Udi.Create(ObjectTypes.GetUdiType(src.NodeObjectType), src.Key)))
|
||||
.ForMember(dest => dest.Icon, opt => opt.MapFrom(src => GetContentTypeIcon(src)))
|
||||
.ForMember(dest => dest.Trashed, opt => opt.MapFrom(src => src.Trashed))
|
||||
.ForMember(dest => dest.Alias, opt => opt.Ignore())
|
||||
.AfterMap((src, dest) =>
|
||||
{
|
||||
if (src.NodeObjectType == Constants.ObjectTypes.Member && dest.Icon.IsNullOrWhiteSpace())
|
||||
{
|
||||
dest.Icon = "icon-user";
|
||||
}
|
||||
});
|
||||
|
||||
CreateMap<PropertyType, EntityBasic>()
|
||||
.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<PropertyGroup, EntityBasic>()
|
||||
.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<IUser, EntityBasic>()
|
||||
.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<ITemplate, EntityBasic>()
|
||||
.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<EntityBasic, ContentTypeSort>()
|
||||
.ForMember(dest => dest.Id, opt => opt.MapFrom(src => new Lazy<int>(() => Convert.ToInt32(src.Id))))
|
||||
.ForMember(dest => dest.SortOrder, opt => opt.Ignore());
|
||||
|
||||
CreateMap<IContentTypeComposition, EntityBasic>()
|
||||
.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<EntitySlim, SearchResultItem>()
|
||||
.ForMember(dest => dest.Udi, opt => opt.MapFrom(src => Udi.Create(ObjectTypes.GetUdiType(src.NodeObjectType), src.Key)))
|
||||
.ForMember(dest => dest.Icon, opt => opt.MapFrom(src=> GetContentTypeIcon(src)))
|
||||
.ForMember(dest => dest.Trashed, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.Alias, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.Score, opt => opt.Ignore())
|
||||
.AfterMap((entity, basic) =>
|
||||
{
|
||||
if (basic.Icon.IsNullOrWhiteSpace())
|
||||
{
|
||||
if (entity.NodeObjectType == Constants.ObjectTypes.Member)
|
||||
basic.Icon = "icon-user";
|
||||
else if (entity.NodeObjectType == Constants.ObjectTypes.DataType)
|
||||
basic.Icon = "icon-autofill";
|
||||
else if (entity.NodeObjectType == Constants.ObjectTypes.DocumentType)
|
||||
basic.Icon = "icon-item-arrangement";
|
||||
else if (entity.NodeObjectType == Constants.ObjectTypes.MediaType)
|
||||
basic.Icon = "icon-thumbnails";
|
||||
else if (entity.NodeObjectType == Constants.ObjectTypes.TemplateType)
|
||||
basic.Icon = "icon-newspaper-alt";
|
||||
}
|
||||
});
|
||||
|
||||
CreateMap<SearchResult, SearchResultItem>()
|
||||
//default to document icon
|
||||
.ForMember(dest => dest.Score, opt => opt.MapFrom(result => result.Score))
|
||||
.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(UmbracoExamineIndexer.IconFieldName)
|
||||
? src.Fields[UmbracoExamineIndexer.IconFieldName]
|
||||
: "icon-document";
|
||||
|
||||
dest.Name = src.Fields.ContainsKey("nodeName") ? src.Fields["nodeName"] : "[no name]";
|
||||
if (src.Fields.ContainsKey(UmbracoExamineIndexer.NodeKeyFieldName))
|
||||
{
|
||||
Guid key;
|
||||
if (Guid.TryParse(src.Fields[UmbracoExamineIndexer.NodeKeyFieldName], out key))
|
||||
{
|
||||
dest.Key = key;
|
||||
|
||||
//need to set the UDI
|
||||
if (src.Fields.ContainsKey(LuceneIndexer.CategoryFieldName))
|
||||
{
|
||||
switch (src.Fields[LuceneIndexer.CategoryFieldName])
|
||||
{
|
||||
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(UmbracoExamineIndexer.IndexPathFieldName) ? src.Fields[UmbracoExamineIndexer.IndexPathFieldName] : "";
|
||||
|
||||
if (src.Fields.ContainsKey(LuceneIndexer.ItemTypeFieldName))
|
||||
{
|
||||
dest.AdditionalData.Add("contentType", src.Fields[LuceneIndexer.ItemTypeFieldName]);
|
||||
}
|
||||
});
|
||||
|
||||
CreateMap<ISearchResults, IEnumerable<SearchResultItem>>()
|
||||
.ConvertUsing(results => results.Select(Mapper.Map<SearchResultItem>).ToList());
|
||||
|
||||
CreateMap<IEnumerable<SearchResult>, IEnumerable<SearchResultItem>>()
|
||||
.ConvertUsing(results => results.Select(Mapper.Map<SearchResultItem>).ToList());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resolves the name for a content item/content variant
|
||||
/// </summary>
|
||||
private class NameResolver : IValueResolver<EntitySlim, EntityBasic, string>
|
||||
{
|
||||
public string Resolve(EntitySlim source, EntityBasic destination, string destMember, ResolutionContext context)
|
||||
{
|
||||
if (!(source is DocumentEntitySlim doc))
|
||||
return source.Name;
|
||||
|
||||
// invariant = only 1 name
|
||||
if (!doc.Variations.VariesByCulture()) return source.Name;
|
||||
|
||||
// variant = depends on culture
|
||||
var culture = context.Options.GetCulture();
|
||||
|
||||
// if there's no culture here, the issue is somewhere else (UI, whatever) - throw!
|
||||
if (culture == null)
|
||||
//throw new InvalidOperationException("Missing culture in mapping options.");
|
||||
// fixme we should throw, but this is used in various places that won't set a culture yet
|
||||
return source.Name;
|
||||
|
||||
// if we don't have a name for a culture, it means the culture is not available, and
|
||||
// hey we should probably not be mapping it, but it's too late, return a fallback name
|
||||
return doc.CultureNames.TryGetValue(culture, out var name) && !name.IsNullOrWhiteSpace() ? name : $"(({source.Name}))";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
177
src/Umbraco.Web/Models/Mapping/EntityModelMapper.cs
Normal file
177
src/Umbraco.Web/Models/Mapping/EntityModelMapper.cs
Normal file
@@ -0,0 +1,177 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using AutoMapper;
|
||||
using Examine;
|
||||
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 : MapperConfiguration
|
||||
{
|
||||
public override void ConfigureMappings(IConfiguration config, ApplicationContext applicationContext)
|
||||
{
|
||||
config.CreateMap<UmbracoEntity, EntityBasic>()
|
||||
.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.MapFrom(x => x.Trashed))
|
||||
.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<PropertyType, EntityBasic>()
|
||||
.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<PropertyGroup, EntityBasic>()
|
||||
.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<IUser, EntityBasic>()
|
||||
.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<ITemplate, EntityBasic>()
|
||||
.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<EntityBasic, ContentTypeSort>()
|
||||
.ForMember(x => x.Id, expression => expression.MapFrom(entity => new Lazy<int>(() => Convert.ToInt32(entity.Id))))
|
||||
.ForMember(x => x.SortOrder, expression => expression.Ignore());
|
||||
|
||||
config.CreateMap<IContentTypeComposition, EntityBasic>()
|
||||
.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<UmbracoEntity, SearchResultItem>()
|
||||
.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())
|
||||
.ForMember(x => x.Score, expression => expression.Ignore())
|
||||
.AfterMap((entity, basic) =>
|
||||
{
|
||||
if (basic.Icon.IsNullOrWhiteSpace())
|
||||
{
|
||||
if (entity.NodeObjectTypeId == Constants.ObjectTypes.MemberGuid)
|
||||
basic.Icon = "icon-user";
|
||||
else if (entity.NodeObjectTypeId == Constants.ObjectTypes.DataTypeGuid)
|
||||
basic.Icon = "icon-autofill";
|
||||
else if (entity.NodeObjectTypeId == Constants.ObjectTypes.DocumentTypeGuid)
|
||||
basic.Icon = "icon-item-arrangement";
|
||||
else if (entity.NodeObjectTypeId == Constants.ObjectTypes.MediaTypeGuid)
|
||||
basic.Icon = "icon-thumbnails";
|
||||
else if (entity.NodeObjectTypeId == Constants.ObjectTypes.TemplateTypeGuid)
|
||||
basic.Icon = "icon-newspaper-alt";
|
||||
}
|
||||
});
|
||||
|
||||
config.CreateMap<SearchResult, SearchResultItem>()
|
||||
//default to document icon
|
||||
.ForMember(x => x.Score, expression => expression.MapFrom(result => result.Score))
|
||||
.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(UmbracoContentIndexer.IconFieldName)
|
||||
? result.Fields[UmbracoContentIndexer.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(UmbracoContentIndexer.NodeTypeAliasFieldName))
|
||||
{
|
||||
basic.AdditionalData.Add("contentType", result.Fields[UmbracoContentIndexer.NodeTypeAliasFieldName]);
|
||||
}
|
||||
});
|
||||
|
||||
config.CreateMap<ISearchResults, IEnumerable<SearchResultItem>>()
|
||||
.ConvertUsing(results => results.Select(Mapper.Map<SearchResultItem>).ToList());
|
||||
|
||||
config.CreateMap<IEnumerable<SearchResult>, IEnumerable<SearchResultItem>>()
|
||||
.ConvertUsing(results => results.Select(Mapper.Map<SearchResultItem>).ToList());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
using AutoMapper;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
using Umbraco.Web.Models.ContentEditing;
|
||||
|
||||
namespace Umbraco.Web.Models.Mapping
|
||||
{
|
||||
/// <summary>
|
||||
/// Mapping extension methods for re-use with other mappers (saves code duplication)
|
||||
/// </summary>
|
||||
internal static class EntityModelMapperExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Ignores readonly properties and the date values
|
||||
/// </summary>
|
||||
/// <param name="mapping"></param>
|
||||
/// <returns></returns>
|
||||
public static IMappingExpression<TSource, TDest> IgnoreDeletableEntityCommonProperties<TSource, TDest>(
|
||||
this IMappingExpression<TSource, TDest> mapping)
|
||||
where TDest: IDeletableEntity
|
||||
{
|
||||
return mapping
|
||||
.IgnoreEntityCommonProperties()
|
||||
.ForMember(dest => dest.DeletedDate, map => map.Ignore());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ignores readonly properties and the date values
|
||||
/// </summary>
|
||||
/// <param name="mapping"></param>
|
||||
/// <returns></returns>
|
||||
public static IMappingExpression<TSource, TDest> IgnoreEntityCommonProperties<TSource, TDest>(
|
||||
this IMappingExpression<TSource, TDest> mapping)
|
||||
where TDest : IEntity
|
||||
{
|
||||
return mapping
|
||||
.IgnoreAllPropertiesWithAnInaccessibleSetter()
|
||||
.ForMember(dest => dest.CreateDate, map => map.Ignore())
|
||||
.ForMember(dest => dest.UpdateDate, map => map.Ignore());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
using AutoMapper;
|
||||
using Umbraco.Core.Models.Entities;
|
||||
|
||||
namespace Umbraco.Web.Models.Mapping
|
||||
{
|
||||
/// <summary>
|
||||
/// Mapping extension methods for re-use with other mappers (saves code duplication)
|
||||
/// </summary>
|
||||
internal static class EntityProfileExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Ignores readonly properties and the date values
|
||||
/// </summary>
|
||||
/// <param name="mapping"></param>
|
||||
/// <returns></returns>
|
||||
public static IMappingExpression<TSource, TDest> IgnoreEntityCommonProperties<TSource, TDest>(this IMappingExpression<TSource, TDest> mapping)
|
||||
where TDest : IEntity
|
||||
{
|
||||
return mapping
|
||||
.IgnoreAllPropertiesWithAnInaccessibleSetter()
|
||||
.ForMember(dest => dest.CreateDate, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.UpdateDate, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.DeleteDate, opt => opt.Ignore());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using AutoMapper;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Web.Models.ContentEditing;
|
||||
using Language = Umbraco.Web.Models.ContentEditing.Language;
|
||||
|
||||
namespace Umbraco.Web.Models.Mapping
|
||||
{
|
||||
internal class LanguageMapperProfile : Profile
|
||||
{
|
||||
public LanguageMapperProfile()
|
||||
{
|
||||
CreateMap<ILanguage, Language>()
|
||||
.ForMember(l => l.Name, expression => expression.MapFrom(x => x.CultureInfo.DisplayName));
|
||||
|
||||
CreateMap<IEnumerable<ILanguage>, IEnumerable<Language>>()
|
||||
.ConvertUsing<LanguageCollectionTypeConverter>();
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a list of <see cref="ILanguage"/> to a list of <see cref="Language"/> and ensures the correct order and defaults are set
|
||||
/// </summary>
|
||||
// ReSharper disable once ClassNeverInstantiated.Local
|
||||
private class LanguageCollectionTypeConverter : ITypeConverter<IEnumerable<ILanguage>, IEnumerable<Language>>
|
||||
{
|
||||
public IEnumerable<Language> Convert(IEnumerable<ILanguage> source, IEnumerable<Language> destination, ResolutionContext context)
|
||||
{
|
||||
var langs = source.Select(x => context.Mapper.Map<ILanguage, Language>(x, null, context)).ToList();
|
||||
|
||||
//Put the default language first in the list & then sort rest by a-z
|
||||
var defaultLang = langs.SingleOrDefault(x => x.IsDefault);
|
||||
|
||||
//Remove the default lang from the list for now
|
||||
langs.Remove(defaultLang);
|
||||
|
||||
//Sort the remaining languages a-z
|
||||
langs = langs.OrderBy(x => x.Name).ToList();
|
||||
|
||||
//Insert the default lang as the first item
|
||||
langs.Insert(0, defaultLang);
|
||||
|
||||
return langs;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,31 +3,30 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Services;
|
||||
|
||||
namespace Umbraco.Web.Models.Mapping
|
||||
{
|
||||
internal class LockedCompositionsResolver
|
||||
internal class LockedCompositionsResolver : ValueResolver<IContentTypeComposition, IEnumerable<string>>
|
||||
{
|
||||
private readonly IContentTypeService _contentTypeService;
|
||||
private readonly ApplicationContext _applicationContext;
|
||||
|
||||
public LockedCompositionsResolver(IContentTypeService contentTypeService)
|
||||
public LockedCompositionsResolver(ApplicationContext applicationContext)
|
||||
{
|
||||
_contentTypeService = contentTypeService;
|
||||
_applicationContext = applicationContext;
|
||||
}
|
||||
|
||||
public IEnumerable<string> Resolve(IContentTypeComposition source)
|
||||
protected override IEnumerable<string> ResolveCore(IContentTypeComposition source)
|
||||
{
|
||||
var aliases = new List<string>();
|
||||
// get ancestor ids from path of parent if not root
|
||||
if (source.ParentId != Constants.System.Root)
|
||||
{
|
||||
var parent = _contentTypeService.Get(source.ParentId);
|
||||
var parent = _applicationContext.Services.ContentTypeService.GetContentType(source.ParentId);
|
||||
if (parent != null)
|
||||
{
|
||||
var ancestorIds = parent.Path.Split(',').Select(int.Parse);
|
||||
// loop through all content types and return ordered aliases of ancestors
|
||||
var allContentTypes = _contentTypeService.GetAll().ToArray();
|
||||
var allContentTypes = _applicationContext.Services.ContentTypeService.GetAllContentTypes().ToArray();
|
||||
foreach (var ancestorId in ancestorIds)
|
||||
{
|
||||
var ancestor = allContentTypes.FirstOrDefault(x => x.Id == ancestorId);
|
||||
|
||||
@@ -1,53 +1,58 @@
|
||||
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
|
||||
{
|
||||
/// <summary>
|
||||
/// Declares model mappings for macros.
|
||||
/// </summary>
|
||||
internal class MacroMapperProfile : Profile
|
||||
{
|
||||
public MacroMapperProfile()
|
||||
{
|
||||
//FROM IMacro TO EntityBasic
|
||||
CreateMap<IMacro, EntityBasic>()
|
||||
.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<IMacro, IEnumerable<MacroParameter>>()
|
||||
.ConvertUsing(macro => macro.Properties.Values.Select(Mapper.Map<MacroParameter>).ToList());
|
||||
|
||||
CreateMap<IMacroProperty, MacroParameter>()
|
||||
.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.Aliases.TextBox];
|
||||
Current.Logger.Warn<MacroMapperProfile>("Could not resolve a parameter editor with alias {PropertyEditorAlias}, a textbox will be rendered in it's place", property.EditorAlias);
|
||||
}
|
||||
|
||||
parameter.View = paramEditor.GetValueEditor().View;
|
||||
//set the config
|
||||
parameter.Configuration = paramEditor.DefaultConfiguration;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
using System;
|
||||
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.Core.PropertyEditors;
|
||||
using Umbraco.Web.Models.ContentEditing;
|
||||
|
||||
namespace Umbraco.Web.Models.Mapping
|
||||
{
|
||||
/// <summary>
|
||||
/// Declares model mappings for macros.
|
||||
/// </summary>
|
||||
internal class MacroModelMapper : MapperConfiguration
|
||||
{
|
||||
public override void ConfigureMappings(IConfiguration config, ApplicationContext applicationContext)
|
||||
{
|
||||
//FROM IMacro TO EntityBasic
|
||||
config.CreateMap<IMacro, EntityBasic>()
|
||||
.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<IMacro, IEnumerable<MacroParameter>>()
|
||||
.ConvertUsing(macro => macro.Properties.Select(Mapper.Map<MacroParameter>).ToList());
|
||||
|
||||
config.CreateMap<IMacroProperty, MacroParameter>()
|
||||
.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 = ParameterEditorResolver.Current.GetByAlias(property.EditorAlias, true);
|
||||
if (paramEditor == null)
|
||||
{
|
||||
//we'll just map this to a text box
|
||||
paramEditor = ParameterEditorResolver.Current.GetByAlias(Constants.PropertyEditors.TextboxAlias);
|
||||
LogHelper.Warn<MacroModelMapper>("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;
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
using AutoMapper;
|
||||
|
||||
namespace Umbraco.Web.Models.Mapping
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides extension methods for AutoMapper's <see cref="IMappingOperationOptions"/>.
|
||||
/// </summary>
|
||||
internal static class MappingOperationOptionsExtensions
|
||||
{
|
||||
private const string CultureKey = "MappingOperationOptions.Culture";
|
||||
private const string IncludedPropertiesKey = "MappingOperationOptions.IncludeProperties";
|
||||
|
||||
/// <summary>
|
||||
/// Gets the context culture.
|
||||
/// </summary>
|
||||
public static string GetCulture(this IMappingOperationOptions options)
|
||||
{
|
||||
return options.Items.TryGetValue(CultureKey, out var obj) && obj is string s ? s : null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets a context culture.
|
||||
/// </summary>
|
||||
public static void SetCulture(this IMappingOperationOptions options, string culture)
|
||||
{
|
||||
options.Items[CultureKey] = culture;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get included properties.
|
||||
/// </summary>
|
||||
public static string[] GetIncludedProperties(this IMappingOperationOptions options)
|
||||
{
|
||||
return options.Items.TryGetValue(IncludedPropertiesKey, out var obj) && obj is string[] s ? s : null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets included properties.
|
||||
/// </summary>
|
||||
public static void SetIncludedProperties(this IMappingOperationOptions options, string[] properties)
|
||||
{
|
||||
options.Items[IncludedPropertiesKey] = properties;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using AutoMapper;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Models.ContentEditing;
|
||||
using Umbraco.Web.ContentApps;
|
||||
using Umbraco.Web.Models.ContentEditing;
|
||||
|
||||
namespace Umbraco.Web.Models.Mapping
|
||||
{
|
||||
// injected into ContentMapperProfile,
|
||||
// maps ContentApps when mapping IMedia to MediaItemDisplay
|
||||
internal class MediaAppResolver : IValueResolver<IMedia, MediaItemDisplay, IEnumerable<ContentApp>>
|
||||
{
|
||||
private readonly ContentAppDefinitionCollection _contentAppDefinitions;
|
||||
|
||||
public MediaAppResolver(ContentAppDefinitionCollection contentAppDefinitions)
|
||||
{
|
||||
_contentAppDefinitions = contentAppDefinitions;
|
||||
}
|
||||
|
||||
public IEnumerable<ContentApp> Resolve(IMedia source, MediaItemDisplay destination, IEnumerable<ContentApp> destMember, ResolutionContext context)
|
||||
{
|
||||
return _contentAppDefinitions.GetContentAppsFor(source);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
using AutoMapper;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Services;
|
||||
using Umbraco.Web.Models.ContentEditing;
|
||||
|
||||
namespace Umbraco.Web.Models.Mapping
|
||||
{
|
||||
internal class MediaChildOfListViewResolver : IValueResolver<IMedia, MediaItemDisplay, bool>
|
||||
{
|
||||
private readonly IMediaService _mediaService;
|
||||
private readonly IMediaTypeService _mediaTypeService;
|
||||
|
||||
public MediaChildOfListViewResolver(IMediaService mediaService, IMediaTypeService mediaTypeService)
|
||||
{
|
||||
_mediaService = mediaService;
|
||||
_mediaTypeService = mediaTypeService;
|
||||
}
|
||||
|
||||
public bool Resolve(IMedia source, MediaItemDisplay destination, bool destMember, ResolutionContext context)
|
||||
{
|
||||
// map the IsChildOfListView (this is actually if it is a descendant of a list view!)
|
||||
var parent = _mediaService.GetParent(source);
|
||||
return parent != null && (parent.ContentType.IsContainer || _mediaTypeService.HasContainerInPath(parent.Path));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,87 +0,0 @@
|
||||
using AutoMapper;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Configuration;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.PropertyEditors;
|
||||
using Umbraco.Core.Services;
|
||||
using Umbraco.Web.Models.ContentEditing;
|
||||
using Umbraco.Web.Trees;
|
||||
|
||||
namespace Umbraco.Web.Models.Mapping
|
||||
{
|
||||
/// <summary>
|
||||
/// Declares model mappings for media.
|
||||
/// </summary>
|
||||
internal class MediaMapperProfile : Profile
|
||||
{
|
||||
public MediaMapperProfile(
|
||||
TabsAndPropertiesResolver<IMedia, MediaItemDisplay> tabsAndPropertiesResolver,
|
||||
ContentTreeNodeUrlResolver<IMedia, MediaTreeController> contentTreeNodeUrlResolver,
|
||||
MediaAppResolver mediaAppResolver,
|
||||
IUserService userService,
|
||||
ILocalizedTextService textService,
|
||||
IDataTypeService dataTypeService,
|
||||
IMediaService mediaService,
|
||||
IMediaTypeService mediaTypeService,
|
||||
ILogger logger)
|
||||
{
|
||||
// create, capture, cache
|
||||
var mediaOwnerResolver = new OwnerResolver<IMedia>(userService);
|
||||
var childOfListViewResolver = new MediaChildOfListViewResolver(mediaService, mediaTypeService);
|
||||
var mediaTypeBasicResolver = new ContentTypeBasicResolver<IMedia, MediaItemDisplay>();
|
||||
|
||||
//FROM IMedia TO MediaItemDisplay
|
||||
CreateMap<IMedia, MediaItemDisplay>()
|
||||
.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.ResolveUsing(childOfListViewResolver))
|
||||
.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.ResolveUsing(contentTreeNodeUrlResolver))
|
||||
.ForMember(dest => dest.Notifications, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.Errors, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.State, opt => opt.UseValue<ContentSavedState?>(null))
|
||||
.ForMember(dest => dest.Edited, 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.Tabs, opt => opt.ResolveUsing(tabsAndPropertiesResolver))
|
||||
.ForMember(dest => dest.AdditionalData, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.ContentType, opt => opt.ResolveUsing(mediaTypeBasicResolver))
|
||||
.ForMember(dest => dest.MediaLink, opt => opt.ResolveUsing(content => string.Join(",", content.GetUrls(UmbracoConfig.For.UmbracoSettings().Content, logger))))
|
||||
.ForMember(dest => dest.ContentApps, opt => opt.ResolveUsing(mediaAppResolver))
|
||||
.ForMember(dest => dest.VariesByCulture, opt => opt.MapFrom(src => src.ContentType.VariesByCulture()));
|
||||
|
||||
|
||||
//FROM IMedia TO ContentItemBasic<ContentPropertyBasic, IMedia>
|
||||
CreateMap<IMedia, ContentItemBasic<ContentPropertyBasic>>()
|
||||
.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.State, opt => opt.UseValue<ContentSavedState?>(null))
|
||||
.ForMember(dest => dest.Edited, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.Updater, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.Alias, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.AdditionalData, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.VariesByCulture, opt => opt.MapFrom(src => src.ContentType.VariesByCulture()));
|
||||
|
||||
|
||||
//FROM IMedia TO ContentItemDto<IMedia>
|
||||
CreateMap<IMedia, ContentPropertyCollectionDto>();
|
||||
//.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.Edited, 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.AdditionalData, opt => opt.Ignore());
|
||||
}
|
||||
}
|
||||
}
|
||||
122
src/Umbraco.Web/Models/Mapping/MediaModelMapper.cs
Normal file
122
src/Umbraco.Web/Models/Mapping/MediaModelMapper.cs
Normal file
@@ -0,0 +1,122 @@
|
||||
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.PropertyEditors;
|
||||
using Umbraco.Core.Services;
|
||||
using Umbraco.Web.Models.ContentEditing;
|
||||
using Umbraco.Web.Trees;
|
||||
|
||||
namespace Umbraco.Web.Models.Mapping
|
||||
{
|
||||
/// <summary>
|
||||
/// Declares model mappings for media.
|
||||
/// </summary>
|
||||
internal class MediaModelMapper : MapperConfiguration
|
||||
{
|
||||
public override void ConfigureMappings(IConfiguration config, ApplicationContext applicationContext)
|
||||
{
|
||||
//FROM IMedia TO MediaItemDisplay
|
||||
config.CreateMap<IMedia, MediaItemDisplay>()
|
||||
.ForMember(display => display.Udi, expression => expression.MapFrom(content => Udi.Create(Constants.UdiEntityType.Media, content.Key)))
|
||||
.ForMember(display => display.Owner, expression => expression.ResolveUsing(new OwnerResolver<IMedia>()))
|
||||
.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.ResolveUsing(new ChildOfListViewResolver(applicationContext.Services.MediaService, applicationContext.Services.ContentTypeService)))
|
||||
.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, opt => opt.ResolveUsing(new ContentTreeNodeUrlResolver<IMedia, MediaTreeController>()))
|
||||
.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<IMedia>(applicationContext.Services.TextService)))
|
||||
.ForMember(display => display.ContentType, expression => expression.ResolveUsing<MediaTypeBasicResolver>())
|
||||
.ForMember(display => display.MediaLink, expression => expression.ResolveUsing(
|
||||
content => string.Join(",", content.GetUrls(UmbracoConfig.For.UmbracoSettings().Content, applicationContext.ProfilingLogger.Logger))))
|
||||
.AfterMap((media, display) =>
|
||||
{
|
||||
if (media.ContentType.IsContainer)
|
||||
{
|
||||
TabsAndPropertiesResolver<IMedia>.AddListView(display, "media", applicationContext.Services.DataTypeService, applicationContext.Services.TextService);
|
||||
}
|
||||
});
|
||||
|
||||
//FROM IMedia TO ContentItemBasic<ContentPropertyBasic, IMedia>
|
||||
config.CreateMap<IMedia, ContentItemBasic<ContentPropertyBasic, IMedia>>()
|
||||
.ForMember(display => display.Udi, expression => expression.MapFrom(content => Udi.Create(Constants.UdiEntityType.Media, content.Key)))
|
||||
.ForMember(dto => dto.Owner, expression => expression.ResolveUsing(new OwnerResolver<IMedia>()))
|
||||
.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<IMedia>
|
||||
config.CreateMap<IMedia, ContentItemDto<IMedia>>()
|
||||
.ForMember(display => display.Udi, expression => expression.MapFrom(content => Udi.Create(Constants.UdiEntityType.Media, content.Key)))
|
||||
.ForMember(dto => dto.Owner, expression => expression.ResolveUsing(new OwnerResolver<IMedia>()))
|
||||
.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 class ChildOfListViewResolver : ValueResolver<IMedia, bool>
|
||||
{
|
||||
private readonly IMediaService _mediaService;
|
||||
private readonly IContentTypeService _contentTypeService;
|
||||
|
||||
public ChildOfListViewResolver(IMediaService mediaService, IContentTypeService contentTypeService)
|
||||
{
|
||||
_mediaService = mediaService;
|
||||
_contentTypeService = contentTypeService;
|
||||
}
|
||||
|
||||
protected override bool ResolveCore(IMedia source)
|
||||
{
|
||||
// map the IsChildOfListView (this is actually if it is a descendant of a list view!)
|
||||
var parent = _mediaService.GetParent(source);
|
||||
return parent != null && (parent.ContentType.IsContainer || _contentTypeService.HasContainerInPath(parent.Path));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Resolves a <see cref="ContentTypeBasic"/> from the <see cref="IContent"/> item and checks if the current user
|
||||
/// has access to see this data
|
||||
/// </summary>
|
||||
private class MediaTypeBasicResolver : ValueResolver<IMedia, ContentTypeBasic>
|
||||
{
|
||||
protected override ContentTypeBasic ResolveCore(IMedia source)
|
||||
{
|
||||
//TODO: We can resolve the UmbracoContext from the IValueResolver options!
|
||||
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 contentTypeBasic = Mapper.Map<ContentTypeBasic>(source.ContentType);
|
||||
return contentTypeBasic;
|
||||
}
|
||||
//no access
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using AutoMapper;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Web.Models.ContentEditing;
|
||||
|
||||
namespace Umbraco.Web.Models.Mapping
|
||||
{
|
||||
/// <summary>
|
||||
/// A resolver to map <see cref="IMember"/> properties to a collection of <see cref="ContentPropertyBasic"/>
|
||||
/// </summary>
|
||||
internal class MemberBasicPropertiesResolver : IValueResolver<IMember, MemberBasic, IEnumerable<ContentPropertyBasic>>
|
||||
{
|
||||
private readonly IUmbracoContextAccessor _umbracoContextAccessor;
|
||||
|
||||
public MemberBasicPropertiesResolver(IUmbracoContextAccessor umbracoContextAccessor)
|
||||
{
|
||||
_umbracoContextAccessor = umbracoContextAccessor ?? throw new System.ArgumentNullException(nameof(umbracoContextAccessor));
|
||||
}
|
||||
|
||||
public IEnumerable<ContentPropertyBasic> Resolve(IMember source, MemberBasic destination, IEnumerable<ContentPropertyBasic> destMember, ResolutionContext context)
|
||||
{
|
||||
var umbracoContext = _umbracoContextAccessor.UmbracoContext;
|
||||
if (umbracoContext == null) throw new InvalidOperationException("Cannot resolve value without an UmbracoContext available");
|
||||
|
||||
var result = Mapper.Map<IEnumerable<Property>, IEnumerable<ContentPropertyBasic>>(
|
||||
// Sort properties so items from different compositions appear in correct order (see U4-9298). Map sorted properties.
|
||||
source.Properties.OrderBy(prop => prop.PropertyType.SortOrder))
|
||||
.ToList();
|
||||
|
||||
var memberType = source.ContentType;
|
||||
|
||||
//now update the IsSensitive value
|
||||
foreach (var prop in result)
|
||||
{
|
||||
//check if this property is flagged as sensitive
|
||||
var isSensitiveProperty = memberType.IsSensitiveProperty(prop.Alias);
|
||||
//check permissions for viewing sensitive data
|
||||
if (isSensitiveProperty && umbracoContext.Security.CurrentUser.HasAccessToSensitiveData() == false)
|
||||
{
|
||||
//mark this property as sensitive
|
||||
prop.IsSensitive = true;
|
||||
//clear the value
|
||||
prop.Value = null;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
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
|
||||
{
|
||||
/// <summary>
|
||||
/// This ensures that the custom membership provider properties are not mapped - these property values are controller by the membership provider
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 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
|
||||
/// </remarks>
|
||||
internal class MemberDtoPropertiesResolver
|
||||
{
|
||||
public IEnumerable<ContentPropertyDto> 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<Property, ContentPropertyDto>);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,153 +0,0 @@
|
||||
using System;
|
||||
using System.Web.Security;
|
||||
using AutoMapper;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Services;
|
||||
using Umbraco.Web.Models.ContentEditing;
|
||||
using Umbraco.Core.Services.Implement;
|
||||
|
||||
namespace Umbraco.Web.Models.Mapping
|
||||
{
|
||||
/// <summary>
|
||||
/// Declares model mappings for members.
|
||||
/// </summary>
|
||||
internal class MemberMapperProfile : Profile
|
||||
{
|
||||
public MemberMapperProfile(
|
||||
MemberTabsAndPropertiesResolver tabsAndPropertiesResolver,
|
||||
MemberTreeNodeUrlResolver memberTreeNodeUrlResolver,
|
||||
MemberBasicPropertiesResolver memberBasicPropertiesResolver,
|
||||
IUserService userService,
|
||||
IMemberTypeService memberTypeService,
|
||||
IMemberService memberService)
|
||||
{
|
||||
// create, capture, cache
|
||||
var memberOwnerResolver = new OwnerResolver<IMember>(userService);
|
||||
var memberProfiderFieldMappingResolver = new MemberProviderFieldResolver();
|
||||
var membershipScenarioMappingResolver = new MembershipScenarioResolver(memberTypeService);
|
||||
var memberDtoPropertiesResolver = new MemberDtoPropertiesResolver();
|
||||
|
||||
//FROM MembershipUser TO MediaItemDisplay - used when using a non-umbraco membership provider
|
||||
CreateMap<MembershipUser, MemberDisplay>().ConvertUsing<MembershipUserTypeConverter>();
|
||||
|
||||
//FROM MembershipUser TO IMember - used when using a non-umbraco membership provider
|
||||
CreateMap<MembershipUser, IMember>()
|
||||
.ConstructUsing(src => MemberService.CreateGenericMembershipProviderMember(src.UserName, src.Email, src.UserName, ""))
|
||||
//we're giving this entity an ID of int.MaxValue - TODO: SD: I can't remember why this mapping is here?
|
||||
.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<Guid>().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.CultureInfos, 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.DeleteDate, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.WriterId, opt => opt.Ignore())
|
||||
//TODO: Support these eventually
|
||||
.ForMember(dest => dest.PasswordQuestion, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.RawPasswordAnswerValue, opt => opt.Ignore());
|
||||
|
||||
//FROM IMember TO MemberDisplay
|
||||
CreateMap<IMember, MemberDisplay>()
|
||||
.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(tabsAndPropertiesResolver))
|
||||
.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.State, opt => opt.UseValue<ContentSavedState?>(null))
|
||||
.ForMember(dest => dest.Edited, 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.ResolveUsing(memberTreeNodeUrlResolver))
|
||||
.ForMember(dest => dest.VariesByCulture, opt => opt.Ignore());
|
||||
|
||||
//FROM IMember TO MemberBasic
|
||||
CreateMap<IMember, MemberBasic>()
|
||||
//we're giving this entity an ID of int.MaxValue - this is kind of a hack to force angular to use the Key instead of the Id in list views
|
||||
.ForMember(dest => dest.Id, opt => opt.MapFrom(src => int.MaxValue))
|
||||
.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.State, opt => opt.UseValue<ContentSavedState?>(null))
|
||||
.ForMember(dest => dest.Edited, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.Updater, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.Alias, opt => opt.Ignore())
|
||||
.ForMember(dto => dto.Properties, expression => expression.ResolveUsing(memberBasicPropertiesResolver))
|
||||
.ForMember(dest => dest.VariesByCulture, opt => opt.Ignore());
|
||||
|
||||
//FROM MembershipUser TO MemberBasic
|
||||
CreateMap<MembershipUser, MemberBasic>()
|
||||
//we're giving this entity an ID of int.MaxValue - TODO: SD: I can't remember why this mapping is here?
|
||||
.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<Guid>().Result.ToString("N")))
|
||||
.ForMember(dest => dest.Owner, opt => opt.UseValue(new UserProfile {Name = "Admin", UserId = -1 }))
|
||||
.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.State, opt => opt.UseValue(ContentSavedState.Draft))
|
||||
.ForMember(dest => dest.Edited, 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.VariesByCulture, opt => opt.Ignore());
|
||||
|
||||
//FROM IMember TO ContentItemDto<IMember>
|
||||
CreateMap<IMember, ContentPropertyCollectionDto>()
|
||||
//.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.Edited, opt => opt.Ignore())
|
||||
//.ForMember(dest => dest.Updater, opt => opt.Ignore())
|
||||
//.ForMember(dest => dest.Icon, opt => opt.Ignore())
|
||||
//.ForMember(dest => dest.Alias, 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<IMemberGroup, MemberGroupDisplay>()
|
||||
.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());
|
||||
}
|
||||
}
|
||||
}
|
||||
539
src/Umbraco.Web/Models/Mapping/MemberModelMapper.cs
Normal file
539
src/Umbraco.Web/Models/Mapping/MemberModelMapper.cs
Normal file
@@ -0,0 +1,539 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Web;
|
||||
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.PropertyEditors;
|
||||
using Umbraco.Core.Security;
|
||||
using UserProfile = Umbraco.Web.Models.ContentEditing.UserProfile;
|
||||
|
||||
namespace Umbraco.Web.Models.Mapping
|
||||
{
|
||||
/// <summary>
|
||||
/// Declares model mappings for members.
|
||||
/// </summary>
|
||||
internal class MemberModelMapper : MapperConfiguration
|
||||
{
|
||||
public override void ConfigureMappings(IConfiguration config, ApplicationContext applicationContext)
|
||||
{
|
||||
//FROM MembershipUser TO MediaItemDisplay - used when using a non-umbraco membership provider
|
||||
config.CreateMap<MembershipUser, MemberDisplay>().ConvertUsing<MembershipUserTypeConverter>();
|
||||
|
||||
//FROM MembershipUser TO IMember - used when using a non-umbraco membership provider
|
||||
config.CreateMap<MembershipUser, IMember>()
|
||||
.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<Guid>().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<IMember, MemberDisplay>()
|
||||
.ForMember(display => display.Udi, expression => expression.MapFrom(content => Udi.Create(Constants.UdiEntityType.Member, content.Key)))
|
||||
.ForMember(display => display.Owner, expression => expression.ResolveUsing(new OwnerResolver<IMember>()))
|
||||
.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(applicationContext.Services.TextService, applicationContext.Services.MemberService, applicationContext.Services.UserService)))
|
||||
.ForMember(display => display.MemberProviderFieldMapping, expression => expression.ResolveUsing(new MemberProviderFieldMappingResolver()))
|
||||
.ForMember(display => display.MembershipScenario,
|
||||
expression => expression.ResolveUsing(new MembershipScenarioMappingResolver(applicationContext.Services.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, opt => opt.ResolveUsing(new MemberTreeNodeUrlResolver()))
|
||||
.ForMember(display => display.HasPublishedVersion, expression => expression.Ignore());
|
||||
//.AfterMap((member, display) => MapGenericCustomProperties(applicationContext.Services.MemberService, applicationContext.Services.UserService, member, display, applicationContext.Services.TextService));
|
||||
|
||||
//FROM IMember TO MemberBasic
|
||||
config.CreateMap<IMember, MemberBasic>()
|
||||
.ForMember(display => display.Udi,
|
||||
expression =>
|
||||
expression.MapFrom(content => Udi.Create(Constants.UdiEntityType.Member, content.Key)))
|
||||
.ForMember(dto => dto.Owner, expression => expression.ResolveUsing(new OwnerResolver<IMember>()))
|
||||
.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())
|
||||
.ForMember(dto => dto.Properties, expression => expression.ResolveUsing(new MemberBasicPropertiesResolver()));
|
||||
|
||||
//FROM MembershipUser TO MemberBasic
|
||||
config.CreateMap<MembershipUser, MemberBasic>()
|
||||
//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<Guid>().Result.ToString("N")))
|
||||
.ForMember(member => member.Owner, expression => expression.UseValue(new UserProfile { 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<IMember>
|
||||
config.CreateMap<IMember, ContentItemDto<IMember>>()
|
||||
.ForMember(display => display.Udi, expression => expression.MapFrom(content => Udi.Create(Constants.UdiEntityType.Member, content.Key)))
|
||||
.ForMember(dto => dto.Owner, expression => expression.ResolveUsing(new OwnerResolver<IMember>()))
|
||||
.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()));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the login property display field
|
||||
/// </summary>
|
||||
/// <param name="memberService"></param>
|
||||
/// <param name="member"></param>
|
||||
/// <param name="localizedText"></param>
|
||||
/// <returns></returns>
|
||||
/// <remarks>
|
||||
/// 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.
|
||||
/// </remarks>
|
||||
internal static ContentPropertyDisplay GetLoginProperty(IMemberService memberService, IMember member, ILocalizedTextService localizedText)
|
||||
{
|
||||
var prop = new ContentPropertyDisplay
|
||||
{
|
||||
Alias = string.Format("{0}login", Constants.PropertyEditors.InternalGenericPropertiesPrefix),
|
||||
Label = localizedText.Localize("login"),
|
||||
Value = member.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<string, bool> 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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This ensures that the custom membership provider properties are not mapped - these property values are controller by the membership provider
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 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
|
||||
/// </remarks>
|
||||
internal class MemberDtoPropertiesValueResolver : ValueResolver<IMember, IEnumerable<ContentPropertyDto>>
|
||||
{
|
||||
protected override IEnumerable<ContentPropertyDto> 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<Property, ContentPropertyDto>);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 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.
|
||||
///
|
||||
/// This also ensures that the IsSensitive property display value is set based on the configured IMemberType property type
|
||||
/// </remarks>
|
||||
internal class MemberTabsAndPropertiesResolver : TabsAndPropertiesResolver<IMember>
|
||||
{
|
||||
private readonly ILocalizedTextService _localizedTextService;
|
||||
private readonly IMemberService _memberService;
|
||||
private readonly IUserService _userService;
|
||||
|
||||
public MemberTabsAndPropertiesResolver(ILocalizedTextService localizedTextService, IMemberService memberService, IUserService userService)
|
||||
: base(localizedTextService)
|
||||
{
|
||||
_localizedTextService = localizedTextService;
|
||||
_memberService = memberService;
|
||||
_userService = userService;
|
||||
}
|
||||
|
||||
public MemberTabsAndPropertiesResolver(ILocalizedTextService localizedTextService,
|
||||
IEnumerable<string> ignoreProperties, IMemberService memberService, IUserService userService) : base(localizedTextService, ignoreProperties)
|
||||
{
|
||||
_localizedTextService = localizedTextService;
|
||||
_memberService = memberService;
|
||||
_userService = userService;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Overridden to deal with custom member properties and permissions
|
||||
/// </summary>
|
||||
/// <param name="umbracoContext"></param>
|
||||
/// <param name="content"></param>
|
||||
/// <returns></returns>
|
||||
protected override List<Tab<ContentPropertyDisplay>> ResolveCore(UmbracoContext umbracoContext, IMember content)
|
||||
{
|
||||
var provider = Core.Security.MembershipProviderExtensions.GetMembersMembershipProvider();
|
||||
|
||||
IgnoreProperties = content.PropertyTypes
|
||||
.Where(x => x.HasIdentity == false)
|
||||
.Select(x => x.Alias)
|
||||
.ToArray();
|
||||
|
||||
var result = base.ResolveCore(umbracoContext, content);
|
||||
|
||||
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?.Value != null && isLockedOutProperty.Value.ToString() != "1")
|
||||
{
|
||||
isLockedOutProperty.View = "readonlyvalue";
|
||||
isLockedOutProperty.Value = _localizedTextService.Localize("general/no");
|
||||
}
|
||||
}
|
||||
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?.Value != null && isLockedOutProperty.Value.ToString() != "1")
|
||||
{
|
||||
isLockedOutProperty.View = "readonlyvalue";
|
||||
isLockedOutProperty.Value = _localizedTextService.Localize("general/no");
|
||||
}
|
||||
}
|
||||
|
||||
if (umbracoContext != null && umbracoContext.Security.CurrentUser != null
|
||||
&& umbracoContext.Security.CurrentUser.AllowedSections.Any(x => x.Equals(Constants.Applications.Settings)))
|
||||
{
|
||||
var memberTypeLink = string.Format("#/member/memberTypes/edit/{0}", content.ContentTypeId);
|
||||
|
||||
//Replace the doctype property
|
||||
var docTypeProperty = result.SelectMany(x => x.Properties)
|
||||
.First(x => x.Alias == string.Format("{0}doctype", Constants.PropertyEditors.InternalGenericPropertiesPrefix));
|
||||
docTypeProperty.Value = new List<object>
|
||||
{
|
||||
new
|
||||
{
|
||||
linkText = content.ContentType.Name,
|
||||
url = memberTypeLink,
|
||||
target = "_self",
|
||||
icon = "icon-item-arrangement"
|
||||
}
|
||||
};
|
||||
docTypeProperty.View = "urllist";
|
||||
}
|
||||
|
||||
//check if there's an approval field
|
||||
var legacyProvider = provider as global::umbraco.providers.members.UmbracoMembershipProvider;
|
||||
if (content.HasIdentity == false && legacyProvider != null)
|
||||
{
|
||||
var approvedField = legacyProvider.ApprovedPropertyTypeAlias;
|
||||
var prop = result.SelectMany(x => x.Properties).FirstOrDefault(x => x.Alias == approvedField);
|
||||
if (prop != null)
|
||||
{
|
||||
prop.Value = 1;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
protected override IEnumerable<ContentPropertyDisplay> GetCustomGenericProperties(IContentBase content)
|
||||
{
|
||||
var member = (IMember) content;
|
||||
var membersProvider = Core.Security.MembershipProviderExtensions.GetMembersMembershipProvider();
|
||||
|
||||
var genericProperties = new List<ContentPropertyDisplay>
|
||||
{
|
||||
new ContentPropertyDisplay
|
||||
{
|
||||
Alias = string.Format("{0}id", Constants.PropertyEditors.InternalGenericPropertiesPrefix),
|
||||
Label = _localizedTextService.Localize("general/id"),
|
||||
Value = new List<string> {member.Id.ToString(), member.Key.ToString()},
|
||||
View = "idwithguid"
|
||||
},
|
||||
new ContentPropertyDisplay
|
||||
{
|
||||
Alias = string.Format("{0}doctype", Constants.PropertyEditors.InternalGenericPropertiesPrefix),
|
||||
Label = _localizedTextService.Localize("content/membertype"),
|
||||
Value = _localizedTextService.UmbracoDictionaryTranslate(member.ContentType.Name),
|
||||
View = PropertyEditorResolver.Current.GetByAlias(Constants.PropertyEditors.NoEditAlias).ValueEditor.View
|
||||
},
|
||||
GetLoginProperty(_memberService, member, _localizedTextService),
|
||||
new ContentPropertyDisplay
|
||||
{
|
||||
Alias = string.Format("{0}email", Constants.PropertyEditors.InternalGenericPropertiesPrefix),
|
||||
Label = _localizedTextService.Localize("general/email"),
|
||||
Value = member.Email,
|
||||
View = "email",
|
||||
Validation = {Mandatory = true}
|
||||
},
|
||||
new ContentPropertyDisplay
|
||||
{
|
||||
Alias = string.Format("{0}password", Constants.PropertyEditors.InternalGenericPropertiesPrefix),
|
||||
Label = _localizedTextService.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<string, object>
|
||||
{
|
||||
{"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<string, object>(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 = _localizedTextService.Localize("content/membergroup"),
|
||||
Value = GetMemberGroupValue(member.Username),
|
||||
View = "membergroups",
|
||||
Config = new Dictionary<string, object> {{"IsRequired", true}}
|
||||
}
|
||||
};
|
||||
|
||||
return genericProperties;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Overridden to assign the IsSensitive property values
|
||||
/// </summary>
|
||||
/// <param name="umbracoContext"></param>
|
||||
/// <param name="content"></param>
|
||||
/// <param name="properties"></param>
|
||||
/// <returns></returns>
|
||||
protected override List<ContentPropertyDisplay> MapProperties(UmbracoContext umbracoContext, IContentBase content, List<Property> properties)
|
||||
{
|
||||
var result = base.MapProperties(umbracoContext, content, properties);
|
||||
var member = (IMember)content;
|
||||
var memberType = member.ContentType;
|
||||
|
||||
//now update the IsSensitive value
|
||||
foreach (var prop in result)
|
||||
{
|
||||
//check if this property is flagged as sensitive
|
||||
var isSensitiveProperty = memberType.IsSensitiveProperty(prop.Alias);
|
||||
//check permissions for viewing sensitive data
|
||||
if (isSensitiveProperty && umbracoContext.Security.CurrentUser.HasAccessToSensitiveData() == false)
|
||||
{
|
||||
//mark this property as sensitive
|
||||
prop.IsSensitive = true;
|
||||
//mark this property as readonly so that it does not post any data
|
||||
prop.Readonly = true;
|
||||
//replace this editor with a sensitivevalue
|
||||
prop.View = "sensitivevalue";
|
||||
//clear the value
|
||||
prop.Value = null;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
internal class MembershipScenarioMappingResolver : ValueResolver<IMember, MembershipScenario>
|
||||
{
|
||||
private readonly IMemberTypeService _memberTypeService;
|
||||
|
||||
public MembershipScenarioMappingResolver(IMemberTypeService memberTypeService)
|
||||
{
|
||||
_memberTypeService = memberTypeService;
|
||||
}
|
||||
|
||||
protected override MembershipScenario ResolveCore(IMember source)
|
||||
{
|
||||
var provider = Core.Security.MembershipProviderExtensions.GetMembersMembershipProvider();
|
||||
|
||||
if (provider.IsUmbracoMembershipProvider())
|
||||
{
|
||||
return MembershipScenario.NativeUmbraco;
|
||||
}
|
||||
var memberType = _memberTypeService.Get(Constants.Conventions.MemberTypes.DefaultAlias);
|
||||
return memberType != null
|
||||
? MembershipScenario.CustomProviderWithUmbracoLink
|
||||
: MembershipScenario.StandaloneCustomProvider;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A resolver to map the provider field aliases
|
||||
/// </summary>
|
||||
internal class MemberProviderFieldMappingResolver : ValueResolver<IMember, IDictionary<string, string>>
|
||||
{
|
||||
protected override IDictionary<string, string> ResolveCore(IMember source)
|
||||
{
|
||||
var provider = Core.Security.MembershipProviderExtensions.GetMembersMembershipProvider();
|
||||
|
||||
if (provider.IsUmbracoMembershipProvider() == false)
|
||||
{
|
||||
return new Dictionary<string, string>
|
||||
{
|
||||
{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<string, string>
|
||||
{
|
||||
{Constants.Conventions.Member.IsLockedOut, umbracoProvider.LockPropertyTypeAlias},
|
||||
{Constants.Conventions.Member.IsApproved, umbracoProvider.ApprovedPropertyTypeAlias},
|
||||
{Constants.Conventions.Member.Comments, umbracoProvider.CommentPropertyTypeAlias}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A converter to go from a <see cref="MembershipUser"/> to a <see cref="MemberDisplay"/>
|
||||
/// </summary>
|
||||
internal class MembershipUserTypeConverter : ITypeConverter<MembershipUser, MemberDisplay>
|
||||
{
|
||||
public MemberDisplay Convert(ResolutionContext context)
|
||||
{
|
||||
var source = (MembershipUser)context.SourceValue;
|
||||
//first convert to IMember
|
||||
var member = Mapper.Map<MembershipUser, IMember>(source);
|
||||
//then convert to MemberDisplay
|
||||
return AutoMapperExtensions.MapWithUmbracoContext<IMember, MemberDisplay>(member, context.GetUmbracoContext());
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A resolver to map <see cref="IMember"/> properties to a collection of <see cref="ContentPropertyBasic"/>
|
||||
/// </summary>
|
||||
internal class MemberBasicPropertiesResolver : IValueResolver
|
||||
{
|
||||
public ResolutionResult Resolve(ResolutionResult source)
|
||||
{
|
||||
if (source.Value != null && (source.Value is IMember) == false)
|
||||
throw new AutoMapperMappingException(string.Format("Value supplied is of type {0} but expected {1}.\nChange the value resolver source type, or redirect the source value supplied to the value resolver using FromMember.", new object[]
|
||||
{
|
||||
source.Value.GetType(),
|
||||
typeof (IMember)
|
||||
}));
|
||||
return source.New(
|
||||
//perform the mapping with the current umbraco context
|
||||
ResolveCore(source.Context.GetUmbracoContext(), (IMember)source.Value), typeof(IEnumerable<ContentPropertyDisplay>));
|
||||
}
|
||||
|
||||
private IEnumerable<ContentPropertyBasic> ResolveCore(UmbracoContext umbracoContext, IMember content)
|
||||
{
|
||||
var result = Mapper.Map<IEnumerable<Property>, IEnumerable<ContentPropertyBasic>>(
|
||||
// Sort properties so items from different compositions appear in correct order (see U4-9298). Map sorted properties.
|
||||
content.Properties.OrderBy(prop => prop.PropertyType.SortOrder))
|
||||
.ToList();
|
||||
|
||||
var member = (IMember)content;
|
||||
var memberType = member.ContentType;
|
||||
|
||||
//now update the IsSensitive value
|
||||
foreach (var prop in result)
|
||||
{
|
||||
//check if this property is flagged as sensitive
|
||||
var isSensitiveProperty = memberType.IsSensitiveProperty(prop.Alias);
|
||||
//check permissions for viewing sensitive data
|
||||
if (isSensitiveProperty && umbracoContext.Security.CurrentUser.HasAccessToSensitiveData() == false)
|
||||
{
|
||||
//mark this property as sensitive
|
||||
prop.IsSensitive = true;
|
||||
//clear the value
|
||||
prop.Value = null;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Security;
|
||||
|
||||
namespace Umbraco.Web.Models.Mapping
|
||||
{
|
||||
/// <summary>
|
||||
/// A resolver to map the provider field aliases
|
||||
/// </summary>
|
||||
internal class MemberProviderFieldResolver
|
||||
{
|
||||
public IDictionary<string, string> Resolve(IMember source)
|
||||
{
|
||||
var provider = Core.Security.MembershipProviderExtensions.GetMembersMembershipProvider();
|
||||
|
||||
if (provider.IsUmbracoMembershipProvider() == false)
|
||||
{
|
||||
return new Dictionary<string, string>
|
||||
{
|
||||
{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<string, string>
|
||||
{
|
||||
{Constants.Conventions.Member.IsLockedOut, umbracoProvider.LockPropertyTypeAlias},
|
||||
{Constants.Conventions.Member.IsApproved, umbracoProvider.ApprovedPropertyTypeAlias},
|
||||
{Constants.Conventions.Member.Comments, umbracoProvider.CommentPropertyTypeAlias}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,270 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Web.Security;
|
||||
using AutoMapper;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Composing;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Models.Membership;
|
||||
using Umbraco.Core.Security;
|
||||
using Umbraco.Core.Services;
|
||||
using Umbraco.Web.Models.ContentEditing;
|
||||
|
||||
namespace Umbraco.Web.Models.Mapping
|
||||
{
|
||||
/// <summary>
|
||||
/// 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
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 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.
|
||||
/// </remarks>
|
||||
internal class MemberTabsAndPropertiesResolver : TabsAndPropertiesResolver<IMember, MemberDisplay>
|
||||
{
|
||||
private readonly IUmbracoContextAccessor _umbracoContextAccessor;
|
||||
private readonly ILocalizedTextService _localizedTextService;
|
||||
private readonly IMemberService _memberService;
|
||||
private readonly IUserService _userService;
|
||||
|
||||
public MemberTabsAndPropertiesResolver(IUmbracoContextAccessor umbracoContextAccessor, ILocalizedTextService localizedTextService, IMemberService memberService, IUserService userService)
|
||||
: base(localizedTextService)
|
||||
{
|
||||
_umbracoContextAccessor = umbracoContextAccessor ?? throw new System.ArgumentNullException(nameof(umbracoContextAccessor));
|
||||
_localizedTextService = localizedTextService ?? throw new System.ArgumentNullException(nameof(localizedTextService));
|
||||
_memberService = memberService ?? throw new System.ArgumentNullException(nameof(memberService));
|
||||
_userService = userService ?? throw new System.ArgumentNullException(nameof(userService));
|
||||
}
|
||||
|
||||
public MemberTabsAndPropertiesResolver(IUmbracoContextAccessor umbracoContextAccessor, ILocalizedTextService localizedTextService, IEnumerable<string> ignoreProperties, IMemberService memberService, IUserService userService)
|
||||
: base(localizedTextService, ignoreProperties)
|
||||
{
|
||||
_umbracoContextAccessor = umbracoContextAccessor ?? throw new System.ArgumentNullException(nameof(umbracoContextAccessor));
|
||||
_localizedTextService = localizedTextService ?? throw new System.ArgumentNullException(nameof(localizedTextService));
|
||||
_memberService = memberService ?? throw new System.ArgumentNullException(nameof(memberService));
|
||||
_userService = userService ?? throw new System.ArgumentNullException(nameof(userService));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <remarks>Overriden to deal with custom member properties and permissions.</remarks>
|
||||
public override IEnumerable<Tab<ContentPropertyDisplay>> Resolve(IMember source, MemberDisplay destination, IEnumerable<Tab<ContentPropertyDisplay>> destMember, ResolutionContext context)
|
||||
{
|
||||
var provider = Core.Security.MembershipProviderExtensions.GetMembersMembershipProvider();
|
||||
|
||||
IgnoreProperties = source.PropertyTypes
|
||||
.Where(x => x.HasIdentity == false)
|
||||
.Select(x => x.Alias)
|
||||
.ToArray();
|
||||
|
||||
var resolved = base.Resolve(source, destination, destMember, context);
|
||||
|
||||
if (provider.IsUmbracoMembershipProvider() == false)
|
||||
{
|
||||
//it's a generic provider so update the locked out property based on our known constant alias
|
||||
var isLockedOutProperty = resolved.SelectMany(x => x.Properties).FirstOrDefault(x => x.Alias == Constants.Conventions.Member.IsLockedOut);
|
||||
if (isLockedOutProperty?.Value != null && isLockedOutProperty.Value.ToString() != "1")
|
||||
{
|
||||
isLockedOutProperty.View = "readonlyvalue";
|
||||
isLockedOutProperty.Value = _localizedTextService.Localize("general/no");
|
||||
}
|
||||
}
|
||||
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 = resolved.SelectMany(x => x.Properties).FirstOrDefault(x => x.Alias == umbracoProvider.LockPropertyTypeAlias);
|
||||
if (isLockedOutProperty?.Value != null && isLockedOutProperty.Value.ToString() != "1")
|
||||
{
|
||||
isLockedOutProperty.View = "readonlyvalue";
|
||||
isLockedOutProperty.Value = _localizedTextService.Localize("general/no");
|
||||
}
|
||||
}
|
||||
|
||||
var umbracoContext = _umbracoContextAccessor.UmbracoContext;
|
||||
if (umbracoContext != null
|
||||
&& umbracoContext.Security.CurrentUser != null
|
||||
&& umbracoContext.Security.CurrentUser.AllowedSections.Any(x => x.Equals(Constants.Applications.Settings)))
|
||||
{
|
||||
var memberTypeLink = string.Format("#/member/memberTypes/edit/{0}", source.ContentTypeId);
|
||||
|
||||
//Replace the doctype property
|
||||
var docTypeProperty = resolved.SelectMany(x => x.Properties)
|
||||
.First(x => x.Alias == string.Format("{0}doctype", Constants.PropertyEditors.InternalGenericPropertiesPrefix));
|
||||
docTypeProperty.Value = new List<object>
|
||||
{
|
||||
new
|
||||
{
|
||||
linkText = source.ContentType.Name,
|
||||
url = memberTypeLink,
|
||||
target = "_self",
|
||||
icon = "icon-item-arrangement"
|
||||
}
|
||||
};
|
||||
docTypeProperty.View = "urllist";
|
||||
}
|
||||
|
||||
return resolved;
|
||||
}
|
||||
|
||||
protected override IEnumerable<ContentPropertyDisplay> GetCustomGenericProperties(IContentBase content)
|
||||
{
|
||||
var member = (IMember)content;
|
||||
var membersProvider = Core.Security.MembershipProviderExtensions.GetMembersMembershipProvider();
|
||||
|
||||
var genericProperties = new List<ContentPropertyDisplay>
|
||||
{
|
||||
new ContentPropertyDisplay
|
||||
{
|
||||
Alias = $"{Constants.PropertyEditors.InternalGenericPropertiesPrefix}id",
|
||||
Label = _localizedTextService.Localize("general/id"),
|
||||
Value = new List<string> {member.Id.ToString(), member.Key.ToString()},
|
||||
View = "idwithguid"
|
||||
},
|
||||
new ContentPropertyDisplay
|
||||
{
|
||||
Alias = $"{Constants.PropertyEditors.InternalGenericPropertiesPrefix}doctype",
|
||||
Label = _localizedTextService.Localize("content/membertype"),
|
||||
Value = _localizedTextService.UmbracoDictionaryTranslate(member.ContentType.Name),
|
||||
View = Current.PropertyEditors[Constants.PropertyEditors.Aliases.NoEdit].GetValueEditor().View
|
||||
},
|
||||
GetLoginProperty(_memberService, member, _localizedTextService),
|
||||
new ContentPropertyDisplay
|
||||
{
|
||||
Alias = $"{Constants.PropertyEditors.InternalGenericPropertiesPrefix}email",
|
||||
Label = _localizedTextService.Localize("general/email"),
|
||||
Value = member.Email,
|
||||
View = "email",
|
||||
Validation = {Mandatory = true}
|
||||
},
|
||||
new ContentPropertyDisplay
|
||||
{
|
||||
Alias = $"{Constants.PropertyEditors.InternalGenericPropertiesPrefix}password",
|
||||
Label = _localizedTextService.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<string, object>
|
||||
{
|
||||
// fixme why ignoreCase, what are we doing here?!
|
||||
{"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<string, object>(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 = $"{Constants.PropertyEditors.InternalGenericPropertiesPrefix}membergroup",
|
||||
Label = _localizedTextService.Localize("content/membergroup"),
|
||||
Value = GetMemberGroupValue(member.Username),
|
||||
View = "membergroups",
|
||||
Config = new Dictionary<string, object> {{"IsRequired", true}}
|
||||
}
|
||||
};
|
||||
|
||||
return genericProperties;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Overridden to assign the IsSensitive property values
|
||||
/// </summary>
|
||||
/// <param name="content"></param>
|
||||
/// <param name="properties"></param>
|
||||
/// <param name="context"></param>
|
||||
/// <returns></returns>
|
||||
protected override List<ContentPropertyDisplay> MapProperties(IContentBase content, List<Property> properties, ResolutionContext context)
|
||||
{
|
||||
var result = base.MapProperties(content, properties, context);
|
||||
var member = (IMember)content;
|
||||
var memberType = member.ContentType;
|
||||
|
||||
var umbracoContext = _umbracoContextAccessor.UmbracoContext;
|
||||
|
||||
//now update the IsSensitive value
|
||||
foreach (var prop in result)
|
||||
{
|
||||
//check if this property is flagged as sensitive
|
||||
var isSensitiveProperty = memberType.IsSensitiveProperty(prop.Alias);
|
||||
//check permissions for viewing sensitive data
|
||||
if (isSensitiveProperty && (umbracoContext == null || umbracoContext.Security.CurrentUser.HasAccessToSensitiveData() == false))
|
||||
{
|
||||
//mark this property as sensitive
|
||||
prop.IsSensitive = true;
|
||||
//mark this property as readonly so that it does not post any data
|
||||
prop.Readonly = true;
|
||||
//replace this editor with a sensitivevalue
|
||||
prop.View = "sensitivevalue";
|
||||
//clear the value
|
||||
prop.Value = null;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the login property display field
|
||||
/// </summary>
|
||||
/// <param name="memberService"></param>
|
||||
/// <param name="member"></param>
|
||||
/// <param name="display"></param>
|
||||
/// <param name="localizedText"></param>
|
||||
/// <returns></returns>
|
||||
/// <remarks>
|
||||
/// 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.
|
||||
/// </remarks>
|
||||
internal static ContentPropertyDisplay GetLoginProperty(IMemberService memberService, IMember member, ILocalizedTextService localizedText)
|
||||
{
|
||||
var prop = new ContentPropertyDisplay
|
||||
{
|
||||
Alias = $"{Constants.PropertyEditors.InternalGenericPropertiesPrefix}login",
|
||||
Label = localizedText.Localize("login"),
|
||||
Value = member.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<string, bool> 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
using System.Web.Mvc;
|
||||
using AutoMapper;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Web.Models.ContentEditing;
|
||||
using Umbraco.Web.Trees;
|
||||
|
||||
namespace Umbraco.Web.Models.Mapping
|
||||
@@ -9,22 +8,25 @@ namespace Umbraco.Web.Models.Mapping
|
||||
/// <summary>
|
||||
/// Gets the tree node url for the IMember
|
||||
/// </summary>
|
||||
internal class MemberTreeNodeUrlResolver : IValueResolver<IMember, MemberDisplay, string>
|
||||
internal class MemberTreeNodeUrlResolver : IValueResolver
|
||||
{
|
||||
private readonly IUmbracoContextAccessor _umbracoContextAccessor;
|
||||
|
||||
public MemberTreeNodeUrlResolver(IUmbracoContextAccessor umbracoContextAccessor)
|
||||
|
||||
public ResolutionResult Resolve(ResolutionResult source)
|
||||
{
|
||||
_umbracoContextAccessor = umbracoContextAccessor ?? throw new System.ArgumentNullException(nameof(umbracoContextAccessor));
|
||||
return source.New(ResolveCore(source, (IMember)source.Value), typeof(string));
|
||||
}
|
||||
|
||||
public string Resolve(IMember source, MemberDisplay destination, string destMember, ResolutionContext context)
|
||||
private string ResolveCore(ResolutionResult res, IMember source)
|
||||
{
|
||||
var umbracoContext = _umbracoContextAccessor.UmbracoContext;
|
||||
if (umbracoContext == null) return null;
|
||||
|
||||
var urlHelper = new UrlHelper(umbracoContext.HttpContext.Request.RequestContext);
|
||||
return urlHelper.GetUmbracoApiService<MemberTreeController>(controller => controller.GetTreeNode(source.Key.ToString("N"), null));
|
||||
var umbCtx = res.Context.GetUmbracoContext();
|
||||
//map the tree node url
|
||||
if (umbCtx != null)
|
||||
{
|
||||
var urlHelper = new UrlHelper(umbCtx.HttpContext.Request.RequestContext);
|
||||
var url = urlHelper.GetUmbracoApiService<MemberTreeController>(controller => controller.GetTreeNode(source.Key.ToString("N"), null));
|
||||
return url;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
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 IMemberTypeService _memberTypeService;
|
||||
|
||||
public MembershipScenarioResolver(IMemberTypeService memberTypeService)
|
||||
{
|
||||
_memberTypeService = memberTypeService;
|
||||
}
|
||||
|
||||
public MembershipScenario Resolve(IMember source)
|
||||
{
|
||||
var provider = Core.Security.MembershipProviderExtensions.GetMembersMembershipProvider();
|
||||
|
||||
if (provider.IsUmbracoMembershipProvider())
|
||||
{
|
||||
return MembershipScenario.NativeUmbraco;
|
||||
}
|
||||
var memberType = _memberTypeService.Get(Constants.Conventions.MemberTypes.DefaultAlias);
|
||||
return memberType != null
|
||||
? MembershipScenario.CustomProviderWithUmbracoLink
|
||||
: MembershipScenario.StandaloneCustomProvider;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
using System.Web.Security;
|
||||
using AutoMapper;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Web.Models.ContentEditing;
|
||||
|
||||
namespace Umbraco.Web.Models.Mapping
|
||||
{
|
||||
/// <summary>
|
||||
/// A converter to go from a <see cref="MembershipUser"/> to a <see cref="MemberDisplay"/>
|
||||
/// </summary>
|
||||
internal class MembershipUserTypeConverter : ITypeConverter<MembershipUser, MemberDisplay>
|
||||
{
|
||||
public MemberDisplay Convert(MembershipUser source, MemberDisplay destination, ResolutionContext context)
|
||||
{
|
||||
//first convert to IMember
|
||||
var member = Mapper.Map<IMember>(source);
|
||||
//then convert to MemberDisplay
|
||||
return Mapper.Map<MemberDisplay>(member);
|
||||
}
|
||||
}
|
||||
}
|
||||
39
src/Umbraco.Web/Models/Mapping/MiscModelsMapper.cs
Normal file
39
src/Umbraco.Web/Models/Mapping/MiscModelsMapper.cs
Normal file
@@ -0,0 +1,39 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using AutoMapper;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Models.Mapping;
|
||||
using Umbraco.Web.Models.ContentEditing;
|
||||
using umbraco.BusinessLogic;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Web.Routing;
|
||||
|
||||
namespace Umbraco.Web.Models.Mapping
|
||||
{
|
||||
/// <summary>
|
||||
/// A model mapper used to map models for the various dashboards
|
||||
/// </summary>
|
||||
internal class MiscModelsMapper : MapperConfiguration
|
||||
{
|
||||
public override void ConfigureMappings(IConfiguration config, ApplicationContext applicationContext)
|
||||
{
|
||||
config.CreateMap<IRedirectUrl, ContentRedirectUrl>()
|
||||
.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));
|
||||
|
||||
//for the logging controller (and assuming dashboard that is used in uaas? otherwise not sure what that controller is used for)
|
||||
config.CreateMap<LogItem, AuditLog>()
|
||||
.ForMember(log => log.UserAvatars, expression => expression.Ignore())
|
||||
.ForMember(log => log.UserName, expression => expression.Ignore())
|
||||
.ForMember(log => log.LogType, expression => expression.MapFrom(item => Enum<AuditType>.Parse(item.LogType.ToString())));
|
||||
|
||||
config.CreateMap<IAuditItem, AuditLog>()
|
||||
.ForMember(log => log.UserAvatars, expression => expression.Ignore())
|
||||
.ForMember(log => log.UserName, expression => expression.Ignore())
|
||||
.ForMember(log => log.NodeId, expression => expression.MapFrom(item => item.Id))
|
||||
.ForMember(log => log.Timestamp, expression => expression.MapFrom(item => item.CreateDate))
|
||||
.ForMember(log => log.LogType, expression => expression.MapFrom(item => item.AuditType));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,30 +1,22 @@
|
||||
using AutoMapper;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Models.Membership;
|
||||
using Umbraco.Core.Services;
|
||||
using UserProfile = Umbraco.Web.Models.ContentEditing.UserProfile;
|
||||
|
||||
namespace Umbraco.Web.Models.Mapping
|
||||
{
|
||||
/// <summary>
|
||||
/// Maps the Owner for IContentBase
|
||||
/// </summary>
|
||||
/// <typeparam name="TPersisted"></typeparam>
|
||||
internal class OwnerResolver<TPersisted>
|
||||
where TPersisted : IContentBase
|
||||
{
|
||||
private readonly IUserService _userService;
|
||||
|
||||
public OwnerResolver(IUserService userService)
|
||||
{
|
||||
_userService = userService;
|
||||
}
|
||||
|
||||
public UserProfile Resolve(TPersisted source)
|
||||
{
|
||||
var profile = source.GetCreatorProfile(_userService);
|
||||
return profile == null ? null : Mapper.Map<IProfile, UserProfile>(profile);
|
||||
}
|
||||
}
|
||||
}
|
||||
using AutoMapper;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
using Umbraco.Core.Models.Membership;
|
||||
using Umbraco.Web.Models.ContentEditing;
|
||||
using UserProfile = Umbraco.Web.Models.ContentEditing.UserProfile;
|
||||
|
||||
namespace Umbraco.Web.Models.Mapping
|
||||
{
|
||||
/// <summary>
|
||||
/// Maps the Owner for IContentBase
|
||||
/// </summary>
|
||||
/// <typeparam name="TPersisted"></typeparam>
|
||||
internal class OwnerResolver<TPersisted> : ValueResolver<TPersisted, UserProfile>
|
||||
where TPersisted : IContentBase
|
||||
{
|
||||
protected override UserProfile ResolveCore(TPersisted source)
|
||||
{
|
||||
return Mapper.Map<IProfile, UserProfile>(source.GetCreatorProfile());
|
||||
}
|
||||
}
|
||||
}
|
||||
84
src/Umbraco.Web/Models/Mapping/PreValueDisplayResolver.cs
Normal file
84
src/Umbraco.Web/Models/Mapping/PreValueDisplayResolver.cs
Normal file
@@ -0,0 +1,84 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using AutoMapper;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.PropertyEditors;
|
||||
using Umbraco.Core.Services;
|
||||
using Umbraco.Web.Models.ContentEditing;
|
||||
|
||||
namespace Umbraco.Web.Models.Mapping
|
||||
{
|
||||
internal class PreValueDisplayResolver : ValueResolver<IDataTypeDefinition, IEnumerable<PreValueFieldDisplay>>
|
||||
{
|
||||
private readonly IDataTypeService _dataTypeService;
|
||||
|
||||
public PreValueDisplayResolver(IDataTypeService dataTypeService)
|
||||
{
|
||||
_dataTypeService = dataTypeService;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Maps pre-values in the dictionary to the values for the fields.
|
||||
/// </summary>
|
||||
/// <param name="fields">The fields.</param>
|
||||
/// <param name="preValues">The pre-values.</param>
|
||||
/// <param name="editorAlias">The editor alias.</param>
|
||||
internal static void MapPreValueValuesToPreValueFields(PreValueFieldDisplay[] fields, IDictionary<string, object> preValues, string editorAlias)
|
||||
{
|
||||
if (fields == null) throw new ArgumentNullException(nameof(fields));
|
||||
if (preValues == null) throw new ArgumentNullException(nameof(preValues));
|
||||
|
||||
// Now we need to wire up the pre-values values with the actual fields defined
|
||||
foreach (var field in fields)
|
||||
{
|
||||
// If the dictionary would be constructed with StringComparer.InvariantCultureIgnoreCase, we could just use TryGetValue
|
||||
var preValue = preValues.SingleOrDefault(x => x.Key.InvariantEquals(field.Key));
|
||||
if (preValue.Key == null)
|
||||
{
|
||||
LogHelper.Warn<PreValueDisplayResolver>("Could not find persisted pre-value for field {0} on property editor {1}", () => field.Key, () => editorAlias);
|
||||
continue;
|
||||
}
|
||||
|
||||
field.Value = preValue.Value;
|
||||
}
|
||||
}
|
||||
|
||||
internal IEnumerable<PreValueFieldDisplay> Convert(IDataTypeDefinition source)
|
||||
{
|
||||
PropertyEditor propEd = null;
|
||||
if (source.PropertyEditorAlias.IsNullOrWhiteSpace() == false)
|
||||
{
|
||||
propEd = PropertyEditorResolver.Current.GetByAlias(source.PropertyEditorAlias);
|
||||
if (propEd == null)
|
||||
{
|
||||
throw new InvalidOperationException("Could not find property editor with alias " + source.PropertyEditorAlias);
|
||||
}
|
||||
}
|
||||
|
||||
// Set up the defaults
|
||||
var dataTypeService = _dataTypeService;
|
||||
var preVals = dataTypeService.GetPreValuesCollectionByDataTypeId(source.Id);
|
||||
IDictionary<string, object> dictionaryVals = preVals.FormatAsDictionary().ToDictionary(x => x.Key, x => (object)x.Value);
|
||||
var result = Enumerable.Empty<PreValueFieldDisplay>().ToArray();
|
||||
|
||||
// If we have a prop editor, then format the pre-values based on it and create it's fields
|
||||
if (propEd != null)
|
||||
{
|
||||
result = propEd.PreValueEditor.Fields.Select(Mapper.Map<PreValueFieldDisplay>).ToArray();
|
||||
dictionaryVals = propEd.PreValueEditor.ConvertDbToEditor(propEd.DefaultPreValues, preVals);
|
||||
}
|
||||
|
||||
MapPreValueValuesToPreValueFields(result, dictionaryVals, source.PropertyEditorAlias);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
protected override IEnumerable<PreValueFieldDisplay> ResolveCore(IDataTypeDefinition source)
|
||||
{
|
||||
return Convert(source);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,14 +5,14 @@ using Umbraco.Web.Models.ContentEditing;
|
||||
|
||||
namespace Umbraco.Web.Models.Mapping
|
||||
{
|
||||
internal class PropertyGroupDisplayResolver<TSource, TPropertyTypeSource, TPropertyTypeDestination>
|
||||
internal class PropertyGroupDisplayResolver<TSource, TPropertyTypeSource, TPropertyTypeDestination> : ValueResolver<TSource, IEnumerable<PropertyGroupDisplay<TPropertyTypeDestination>>>
|
||||
where TSource : ContentTypeSave<TPropertyTypeSource>
|
||||
where TPropertyTypeDestination : PropertyTypeDisplay
|
||||
where TPropertyTypeDestination : PropertyTypeDisplay
|
||||
where TPropertyTypeSource : PropertyTypeBasic
|
||||
{
|
||||
public IEnumerable<PropertyGroupDisplay<TPropertyTypeDestination>> Resolve(TSource source)
|
||||
protected override IEnumerable<PropertyGroupDisplay<TPropertyTypeDestination>> ResolveCore(TSource source)
|
||||
{
|
||||
return source.Groups.Select(Mapper.Map<PropertyGroupDisplay<TPropertyTypeDestination>>);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,231 +1,229 @@
|
||||
using AutoMapper;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.PropertyEditors;
|
||||
using Umbraco.Core.Services;
|
||||
using Umbraco.Web.Models.ContentEditing;
|
||||
|
||||
namespace Umbraco.Web.Models.Mapping
|
||||
{
|
||||
internal class PropertyTypeGroupResolver<TPropertyType>
|
||||
where TPropertyType : PropertyTypeDisplay, new()
|
||||
{
|
||||
private readonly PropertyEditorCollection _propertyEditors;
|
||||
private readonly IDataTypeService _dataTypeService;
|
||||
|
||||
public PropertyTypeGroupResolver(PropertyEditorCollection propertyEditors, IDataTypeService dataTypeService)
|
||||
{
|
||||
_propertyEditors = propertyEditors;
|
||||
_dataTypeService = dataTypeService;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the content type that defines a property group, within a composition.
|
||||
/// </summary>
|
||||
/// <param name="contentType">The composition.</param>
|
||||
/// <param name="propertyGroupId">The identifier of the property group.</param>
|
||||
/// <returns>The composition content type that defines the specified property group.</returns>
|
||||
private static IContentTypeComposition GetContentTypeForPropertyGroup(IContentTypeComposition contentType, int propertyGroupId)
|
||||
{
|
||||
// test local groups
|
||||
if (contentType.PropertyGroups.Any(x => x.Id == propertyGroupId))
|
||||
return contentType;
|
||||
|
||||
// test composition types groups
|
||||
// .ContentTypeComposition is just the local ones, not recursive,
|
||||
// so we have to recurse here
|
||||
return contentType.ContentTypeComposition
|
||||
.Select(x => GetContentTypeForPropertyGroup(x, propertyGroupId))
|
||||
.FirstOrDefault(x => x != null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the content type that defines a property group, within a composition.
|
||||
/// </summary>
|
||||
/// <param name="contentType">The composition.</param>
|
||||
/// <param name="propertyTypeId">The identifier of the property type.</param>
|
||||
/// <returns>The composition content type that defines the specified property group.</returns>
|
||||
private static IContentTypeComposition GetContentTypeForPropertyType(IContentTypeComposition contentType, int propertyTypeId)
|
||||
{
|
||||
// test local property types
|
||||
if (contentType.PropertyTypes.Any(x => x.Id == propertyTypeId))
|
||||
return contentType;
|
||||
|
||||
// test composition property types
|
||||
// .ContentTypeComposition is just the local ones, not recursive,
|
||||
// so we have to recurse here
|
||||
return contentType.ContentTypeComposition
|
||||
.Select(x => GetContentTypeForPropertyType(x, propertyTypeId))
|
||||
.FirstOrDefault(x => x != null);
|
||||
}
|
||||
|
||||
public IEnumerable<PropertyGroupDisplay<TPropertyType>> Resolve(IContentTypeComposition source)
|
||||
{
|
||||
// deal with groups
|
||||
var groups = new List<PropertyGroupDisplay<TPropertyType>>();
|
||||
|
||||
// add groups local to this content type
|
||||
foreach (var tab in source.PropertyGroups)
|
||||
{
|
||||
var group = new PropertyGroupDisplay<TPropertyType>
|
||||
{
|
||||
Id = tab.Id,
|
||||
Inherited = false,
|
||||
Name = tab.Name,
|
||||
SortOrder = tab.SortOrder,
|
||||
ContentTypeId = source.Id
|
||||
};
|
||||
|
||||
group.Properties = MapProperties(tab.PropertyTypes, source, tab.Id, false);
|
||||
groups.Add(group);
|
||||
}
|
||||
|
||||
// add groups inherited through composition
|
||||
var localGroupIds = groups.Select(x => x.Id).ToArray();
|
||||
foreach (var tab in source.CompositionPropertyGroups)
|
||||
{
|
||||
// skip those that are local to this content type
|
||||
if (localGroupIds.Contains(tab.Id)) continue;
|
||||
|
||||
// get the content type that defines this group
|
||||
var definingContentType = GetContentTypeForPropertyGroup(source, tab.Id);
|
||||
if (definingContentType == null)
|
||||
throw new Exception("PropertyGroup with id=" + tab.Id + " was not found on any of the content type's compositions.");
|
||||
|
||||
var group = new PropertyGroupDisplay<TPropertyType>
|
||||
{
|
||||
Id = tab.Id,
|
||||
Inherited = true,
|
||||
Name = tab.Name,
|
||||
SortOrder = tab.SortOrder,
|
||||
ContentTypeId = definingContentType.Id,
|
||||
ParentTabContentTypes = new[] { definingContentType.Id },
|
||||
ParentTabContentTypeNames = new[] { definingContentType.Name }
|
||||
};
|
||||
|
||||
group.Properties = MapProperties(tab.PropertyTypes, definingContentType, tab.Id, true);
|
||||
groups.Add(group);
|
||||
}
|
||||
|
||||
// deal with generic properties
|
||||
var genericProperties = new List<TPropertyType>();
|
||||
|
||||
// add generic properties local to this content type
|
||||
var entityGenericProperties = source.PropertyTypes.Where(x => x.PropertyGroupId == null);
|
||||
genericProperties.AddRange(MapProperties(entityGenericProperties, source, PropertyGroupBasic.GenericPropertiesGroupId, false));
|
||||
|
||||
// add generic properties inherited through compositions
|
||||
var localGenericPropertyIds = genericProperties.Select(x => x.Id).ToArray();
|
||||
var compositionGenericProperties = source.CompositionPropertyTypes
|
||||
.Where(x => x.PropertyGroupId == null // generic
|
||||
&& localGenericPropertyIds.Contains(x.Id) == false); // skip those that are local
|
||||
foreach (var compositionGenericProperty in compositionGenericProperties)
|
||||
{
|
||||
var definingContentType = GetContentTypeForPropertyType(source, compositionGenericProperty.Id);
|
||||
if (definingContentType == null)
|
||||
throw new Exception("PropertyType with id=" + compositionGenericProperty.Id + " was not found on any of the content type's compositions.");
|
||||
genericProperties.AddRange(MapProperties(new [] { compositionGenericProperty }, definingContentType, PropertyGroupBasic.GenericPropertiesGroupId, true));
|
||||
}
|
||||
|
||||
// if there are any generic properties, add the corresponding tab
|
||||
if (genericProperties.Any())
|
||||
{
|
||||
var genericTab = new PropertyGroupDisplay<TPropertyType>
|
||||
{
|
||||
Id = PropertyGroupBasic.GenericPropertiesGroupId,
|
||||
Name = "Generic properties",
|
||||
ContentTypeId = source.Id,
|
||||
SortOrder = 999,
|
||||
Inherited = false,
|
||||
Properties = genericProperties
|
||||
};
|
||||
groups.Add(genericTab);
|
||||
}
|
||||
|
||||
// handle locked properties
|
||||
var lockedPropertyAliases = new List<string>();
|
||||
// add built-in member property aliases to list of aliases to be locked
|
||||
foreach (var propertyAlias in Constants.Conventions.Member.GetStandardPropertyTypeStubs().Keys)
|
||||
{
|
||||
lockedPropertyAliases.Add(propertyAlias);
|
||||
}
|
||||
// lock properties by aliases
|
||||
foreach (var property in groups.SelectMany(x => x.Properties))
|
||||
{
|
||||
property.Locked = lockedPropertyAliases.Contains(property.Alias);
|
||||
}
|
||||
|
||||
// now merge tabs based on names
|
||||
// as for one name, we might have one local tab, plus some inherited tabs
|
||||
var groupsGroupsByName = groups.GroupBy(x => x.Name).ToArray();
|
||||
groups = new List<PropertyGroupDisplay<TPropertyType>>(); // start with a fresh list
|
||||
foreach (var groupsByName in groupsGroupsByName)
|
||||
{
|
||||
// single group, just use it
|
||||
if (groupsByName.Count() == 1)
|
||||
{
|
||||
groups.Add(groupsByName.First());
|
||||
continue;
|
||||
}
|
||||
|
||||
// multiple groups, merge
|
||||
var group = groupsByName.FirstOrDefault(x => x.Inherited == false) // try local
|
||||
?? groupsByName.First(); // else pick one randomly
|
||||
groups.Add(group);
|
||||
|
||||
// in case we use the local one, flag as inherited
|
||||
group.Inherited = true;
|
||||
|
||||
// merge (and sort) properties
|
||||
var properties = groupsByName.SelectMany(x => x.Properties).OrderBy(x => x.SortOrder).ToArray();
|
||||
group.Properties = properties;
|
||||
|
||||
// collect parent group info
|
||||
var parentGroups = groupsByName.Where(x => x.ContentTypeId != source.Id).ToArray();
|
||||
group.ParentTabContentTypes = parentGroups.SelectMany(x => x.ParentTabContentTypes).ToArray();
|
||||
group.ParentTabContentTypeNames = parentGroups.SelectMany(x => x.ParentTabContentTypeNames).ToArray();
|
||||
}
|
||||
|
||||
return groups.OrderBy(x => x.SortOrder);
|
||||
}
|
||||
|
||||
private IEnumerable<TPropertyType> MapProperties(IEnumerable<PropertyType> properties, IContentTypeBase contentType, int groupId, bool inherited)
|
||||
{
|
||||
var mappedProperties = new List<TPropertyType>();
|
||||
|
||||
foreach (var p in properties.Where(x => x.DataTypeId != 0).OrderBy(x => x.SortOrder))
|
||||
{
|
||||
var propertyEditor = _propertyEditors[p.PropertyEditorAlias];
|
||||
var configuration = _dataTypeService.GetDataType(p.DataTypeId).Configuration;
|
||||
|
||||
if (propertyEditor == null)
|
||||
throw new InvalidOperationException("No property editor could be resolved with the alias: " + p.PropertyEditorAlias + ", ensure all packages are installed correctly.");
|
||||
|
||||
mappedProperties.Add(new TPropertyType
|
||||
{
|
||||
Id = p.Id,
|
||||
Alias = p.Alias,
|
||||
Description = p.Description,
|
||||
Editor = p.PropertyEditorAlias,
|
||||
Validation = new PropertyTypeValidation {Mandatory = p.Mandatory, Pattern = p.ValidationRegExp},
|
||||
Label = p.Name,
|
||||
View = propertyEditor.GetValueEditor().View,
|
||||
Config = propertyEditor.GetConfigurationEditor().ToConfigurationEditor(configuration),
|
||||
//Value = "",
|
||||
GroupId = groupId,
|
||||
Inherited = inherited,
|
||||
DataTypeId = p.DataTypeId,
|
||||
SortOrder = p.SortOrder,
|
||||
ContentTypeId = contentType.Id,
|
||||
ContentTypeName = contentType.Name,
|
||||
AllowCultureVariant = p.VariesByCulture()
|
||||
});
|
||||
}
|
||||
|
||||
return mappedProperties;
|
||||
}
|
||||
}
|
||||
}
|
||||
using AutoMapper;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.PropertyEditors;
|
||||
using Umbraco.Web.Models.ContentEditing;
|
||||
|
||||
namespace Umbraco.Web.Models.Mapping
|
||||
{
|
||||
internal class PropertyTypeGroupResolver<TPropertyType> : ValueResolver<IContentTypeComposition, IEnumerable<PropertyGroupDisplay<TPropertyType>>>
|
||||
where TPropertyType : PropertyTypeDisplay, new()
|
||||
{
|
||||
private readonly ApplicationContext _applicationContext;
|
||||
private readonly Lazy<PropertyEditorResolver> _propertyEditorResolver;
|
||||
|
||||
public PropertyTypeGroupResolver(ApplicationContext applicationContext, Lazy<PropertyEditorResolver> propertyEditorResolver)
|
||||
{
|
||||
_applicationContext = applicationContext;
|
||||
_propertyEditorResolver = propertyEditorResolver;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the content type that defines a property group, within a composition.
|
||||
/// </summary>
|
||||
/// <param name="contentType">The composition.</param>
|
||||
/// <param name="propertyGroupId">The identifier of the property group.</param>
|
||||
/// <returns>The composition content type that defines the specified property group.</returns>
|
||||
private static IContentTypeComposition GetContentTypeForPropertyGroup(IContentTypeComposition contentType, int propertyGroupId)
|
||||
{
|
||||
// test local groups
|
||||
if (contentType.PropertyGroups.Any(x => x.Id == propertyGroupId))
|
||||
return contentType;
|
||||
|
||||
// test composition types groups
|
||||
// .ContentTypeComposition is just the local ones, not recursive,
|
||||
// so we have to recurse here
|
||||
return contentType.ContentTypeComposition
|
||||
.Select(x => GetContentTypeForPropertyGroup(x, propertyGroupId))
|
||||
.FirstOrDefault(x => x != null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the content type that defines a property group, within a composition.
|
||||
/// </summary>
|
||||
/// <param name="contentType">The composition.</param>
|
||||
/// <param name="propertyTypeId">The identifier of the property type.</param>
|
||||
/// <returns>The composition content type that defines the specified property group.</returns>
|
||||
private static IContentTypeComposition GetContentTypeForPropertyType(IContentTypeComposition contentType, int propertyTypeId)
|
||||
{
|
||||
// test local property types
|
||||
if (contentType.PropertyTypes.Any(x => x.Id == propertyTypeId))
|
||||
return contentType;
|
||||
|
||||
// test composition property types
|
||||
// .ContentTypeComposition is just the local ones, not recursive,
|
||||
// so we have to recurse here
|
||||
return contentType.ContentTypeComposition
|
||||
.Select(x => GetContentTypeForPropertyType(x, propertyTypeId))
|
||||
.FirstOrDefault(x => x != null);
|
||||
}
|
||||
|
||||
protected override IEnumerable<PropertyGroupDisplay<TPropertyType>> ResolveCore(IContentTypeComposition source)
|
||||
{
|
||||
// deal with groups
|
||||
var groups = new List<PropertyGroupDisplay<TPropertyType>>();
|
||||
|
||||
// add groups local to this content type
|
||||
foreach (var tab in source.PropertyGroups)
|
||||
{
|
||||
var group = new PropertyGroupDisplay<TPropertyType>
|
||||
{
|
||||
Id = tab.Id,
|
||||
Inherited = false,
|
||||
Name = tab.Name,
|
||||
SortOrder = tab.SortOrder,
|
||||
ContentTypeId = source.Id
|
||||
};
|
||||
|
||||
group.Properties = MapProperties(tab.PropertyTypes, source, tab.Id, false);
|
||||
groups.Add(group);
|
||||
}
|
||||
|
||||
// add groups inherited through composition
|
||||
var localGroupIds = groups.Select(x => x.Id).ToArray();
|
||||
foreach (var tab in source.CompositionPropertyGroups)
|
||||
{
|
||||
// skip those that are local to this content type
|
||||
if (localGroupIds.Contains(tab.Id)) continue;
|
||||
|
||||
// get the content type that defines this group
|
||||
var definingContentType = GetContentTypeForPropertyGroup(source, tab.Id);
|
||||
if (definingContentType == null)
|
||||
throw new Exception("PropertyGroup with id=" + tab.Id + " was not found on any of the content type's compositions.");
|
||||
|
||||
var group = new PropertyGroupDisplay<TPropertyType>
|
||||
{
|
||||
Id = tab.Id,
|
||||
Inherited = true,
|
||||
Name = tab.Name,
|
||||
SortOrder = tab.SortOrder,
|
||||
ContentTypeId = definingContentType.Id,
|
||||
ParentTabContentTypes = new[] { definingContentType.Id },
|
||||
ParentTabContentTypeNames = new[] { definingContentType.Name }
|
||||
};
|
||||
|
||||
group.Properties = MapProperties(tab.PropertyTypes, definingContentType, tab.Id, true);
|
||||
groups.Add(group);
|
||||
}
|
||||
|
||||
// deal with generic properties
|
||||
var genericProperties = new List<TPropertyType>();
|
||||
|
||||
// add generic properties local to this content type
|
||||
var entityGenericProperties = source.PropertyTypes.Where(x => x.PropertyGroupId == null);
|
||||
genericProperties.AddRange(MapProperties(entityGenericProperties, source, PropertyGroupBasic.GenericPropertiesGroupId, false));
|
||||
|
||||
// add generic properties inherited through compositions
|
||||
var localGenericPropertyIds = genericProperties.Select(x => x.Id).ToArray();
|
||||
var compositionGenericProperties = source.CompositionPropertyTypes
|
||||
.Where(x => x.PropertyGroupId == null // generic
|
||||
&& localGenericPropertyIds.Contains(x.Id) == false); // skip those that are local
|
||||
foreach (var compositionGenericProperty in compositionGenericProperties)
|
||||
{
|
||||
var definingContentType = GetContentTypeForPropertyType(source, compositionGenericProperty.Id);
|
||||
if (definingContentType == null)
|
||||
throw new Exception("PropertyType with id=" + compositionGenericProperty.Id + " was not found on any of the content type's compositions.");
|
||||
genericProperties.AddRange(MapProperties(new [] { compositionGenericProperty }, definingContentType, PropertyGroupBasic.GenericPropertiesGroupId, true));
|
||||
}
|
||||
|
||||
// if there are any generic properties, add the corresponding tab
|
||||
if (genericProperties.Any())
|
||||
{
|
||||
var genericTab = new PropertyGroupDisplay<TPropertyType>
|
||||
{
|
||||
Id = PropertyGroupBasic.GenericPropertiesGroupId,
|
||||
Name = "Generic properties",
|
||||
ContentTypeId = source.Id,
|
||||
SortOrder = 999,
|
||||
Inherited = false,
|
||||
Properties = genericProperties
|
||||
};
|
||||
groups.Add(genericTab);
|
||||
}
|
||||
|
||||
// handle locked properties
|
||||
var lockedPropertyAliases = new List<string>();
|
||||
// add built-in member property aliases to list of aliases to be locked
|
||||
foreach (var propertyAlias in Constants.Conventions.Member.GetStandardPropertyTypeStubs().Keys)
|
||||
{
|
||||
lockedPropertyAliases.Add(propertyAlias);
|
||||
}
|
||||
// lock properties by aliases
|
||||
foreach (var property in groups.SelectMany(x => x.Properties))
|
||||
{
|
||||
property.Locked = lockedPropertyAliases.Contains(property.Alias);
|
||||
}
|
||||
|
||||
// now merge tabs based on names
|
||||
// as for one name, we might have one local tab, plus some inherited tabs
|
||||
var groupsGroupsByName = groups.GroupBy(x => x.Name).ToArray();
|
||||
groups = new List<PropertyGroupDisplay<TPropertyType>>(); // start with a fresh list
|
||||
foreach (var groupsByName in groupsGroupsByName)
|
||||
{
|
||||
// single group, just use it
|
||||
if (groupsByName.Count() == 1)
|
||||
{
|
||||
groups.Add(groupsByName.First());
|
||||
continue;
|
||||
}
|
||||
|
||||
// multiple groups, merge
|
||||
var group = groupsByName.FirstOrDefault(x => x.Inherited == false) // try local
|
||||
?? groupsByName.First(); // else pick one randomly
|
||||
groups.Add(group);
|
||||
|
||||
// in case we use the local one, flag as inherited
|
||||
group.Inherited = true;
|
||||
|
||||
// merge (and sort) properties
|
||||
var properties = groupsByName.SelectMany(x => x.Properties).OrderBy(x => x.SortOrder).ToArray();
|
||||
group.Properties = properties;
|
||||
|
||||
// collect parent group info
|
||||
var parentGroups = groupsByName.Where(x => x.ContentTypeId != source.Id).ToArray();
|
||||
group.ParentTabContentTypes = parentGroups.SelectMany(x => x.ParentTabContentTypes).ToArray();
|
||||
group.ParentTabContentTypeNames = parentGroups.SelectMany(x => x.ParentTabContentTypeNames).ToArray();
|
||||
}
|
||||
|
||||
return groups.OrderBy(x => x.SortOrder);
|
||||
}
|
||||
|
||||
private IEnumerable<TPropertyType> MapProperties(IEnumerable<PropertyType> properties, IContentTypeBase contentType, int groupId, bool inherited)
|
||||
{
|
||||
var mappedProperties = new List<TPropertyType>();
|
||||
|
||||
foreach (var p in properties.Where(x => x.DataTypeDefinitionId != 0).OrderBy(x => x.SortOrder))
|
||||
{
|
||||
var propertyEditor = _propertyEditorResolver.Value.GetByAlias(p.PropertyEditorAlias);
|
||||
var preValues = _applicationContext.Services.DataTypeService.GetPreValuesCollectionByDataTypeId(p.DataTypeDefinitionId);
|
||||
|
||||
if (propertyEditor == null)
|
||||
throw new InvalidOperationException("No property editor could be resolved with the alias: " + p.PropertyEditorAlias + ", ensure all packages are installed correctly.");
|
||||
|
||||
mappedProperties.Add(new TPropertyType
|
||||
{
|
||||
Id = p.Id,
|
||||
Alias = p.Alias,
|
||||
Description = p.Description,
|
||||
Editor = p.PropertyEditorAlias,
|
||||
Validation = new PropertyTypeValidation {Mandatory = p.Mandatory, Pattern = p.ValidationRegExp},
|
||||
Label = p.Name,
|
||||
View = propertyEditor.ValueEditor.View,
|
||||
Config = propertyEditor.PreValueEditor.ConvertDbToEditor(propertyEditor.DefaultPreValues, preValues),
|
||||
//Value = "",
|
||||
GroupId = groupId,
|
||||
Inherited = inherited,
|
||||
DataTypeId = p.DataTypeDefinitionId,
|
||||
SortOrder = p.SortOrder,
|
||||
ContentTypeId = contentType.Id,
|
||||
ContentTypeName = contentType.Name
|
||||
});
|
||||
}
|
||||
|
||||
return mappedProperties;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
using AutoMapper;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Web.Models.ContentEditing;
|
||||
using ContentVariation = Umbraco.Core.Models.ContentVariation;
|
||||
|
||||
namespace Umbraco.Web.Models.Mapping
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns the <see cref="ContentVariation"/> for a <see cref="PropertyType"/>
|
||||
/// </summary>
|
||||
internal class PropertyTypeVariationsResolver: IValueResolver<PropertyTypeBasic, PropertyType, ContentVariation>
|
||||
{
|
||||
public ContentVariation Resolve(PropertyTypeBasic source, PropertyType destination, ContentVariation destMember, ResolutionContext context)
|
||||
{
|
||||
return source.AllowCultureVariant ? ContentVariation.Culture : ContentVariation.Nothing;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
using AutoMapper;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Web.Composing;
|
||||
using Umbraco.Web.Models.ContentEditing;
|
||||
using Umbraco.Web.Routing;
|
||||
|
||||
namespace Umbraco.Web.Models.Mapping
|
||||
{
|
||||
internal class RedirectUrlMapperProfile : Profile
|
||||
{
|
||||
private readonly UrlProvider _urlProvider;
|
||||
|
||||
public RedirectUrlMapperProfile(UrlProvider urlProvider)
|
||||
{
|
||||
_urlProvider = urlProvider;
|
||||
}
|
||||
|
||||
public RedirectUrlMapperProfile()
|
||||
{
|
||||
CreateMap<IRedirectUrl, ContentRedirectUrl>()
|
||||
.ForMember(x => x.OriginalUrl, expression => expression.MapFrom(item => _urlProvider.GetUrlFromRoute(item.ContentId, item.Url, null)))
|
||||
.ForMember(x => x.DestinationUrl, expression => expression.Ignore())
|
||||
.ForMember(x => x.RedirectId, expression => expression.MapFrom(item => item.Key));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
using AutoMapper;
|
||||
using Umbraco.Core.Models;
|
||||
using Relation = Umbraco.Web.Models.ContentEditing.Relation;
|
||||
using RelationType = Umbraco.Web.Models.ContentEditing.RelationType;
|
||||
|
||||
namespace Umbraco.Web.Models.Mapping
|
||||
{
|
||||
internal class RelationMapperProfile : Profile
|
||||
{
|
||||
public RelationMapperProfile()
|
||||
{
|
||||
//FROM IRelationType TO RelationType
|
||||
CreateMap<IRelationType, RelationType>();
|
||||
|
||||
//FROM IRelation TO Relation
|
||||
CreateMap<IRelation, Relation>();
|
||||
}
|
||||
}
|
||||
}
|
||||
21
src/Umbraco.Web/Models/Mapping/RelationModelMapper.cs
Normal file
21
src/Umbraco.Web/Models/Mapping/RelationModelMapper.cs
Normal file
@@ -0,0 +1,21 @@
|
||||
using AutoMapper;
|
||||
using Umbraco.Core;
|
||||
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 : MapperConfiguration
|
||||
{
|
||||
public override void ConfigureMappings(IConfiguration config, ApplicationContext applicationContext)
|
||||
{
|
||||
//FROM IRelationType TO RelationType
|
||||
config.CreateMap<IRelationType, RelationType>();
|
||||
|
||||
//FROM IRelation TO Relation
|
||||
config.CreateMap<IRelation, Relation>();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
using System;
|
||||
using AutoMapper;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Web.Models.ContentEditing;
|
||||
|
||||
namespace Umbraco.Web.Models.Mapping
|
||||
{
|
||||
internal class ScheduledPublishDateResolver : IValueResolver<IContent, ContentVariantDisplay, DateTime?>
|
||||
{
|
||||
private readonly ContentScheduleAction _changeType;
|
||||
|
||||
public ScheduledPublishDateResolver(ContentScheduleAction changeType)
|
||||
{
|
||||
_changeType = changeType;
|
||||
}
|
||||
|
||||
public DateTime? Resolve(IContent source, ContentVariantDisplay destination, DateTime? destMember, ResolutionContext context)
|
||||
{
|
||||
var culture = context.Options.GetCulture();
|
||||
var sched = source.ContentSchedule.GetSchedule(culture ?? string.Empty, _changeType);
|
||||
foreach(var s in sched)
|
||||
return s.Date; // take the first, it's ordered by date
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using AutoMapper;
|
||||
using Umbraco.Core.Services;
|
||||
using Umbraco.Web.Models.ContentEditing;
|
||||
|
||||
namespace Umbraco.Web.Models.Mapping
|
||||
{
|
||||
internal class SectionMapperProfile : Profile
|
||||
{
|
||||
public SectionMapperProfile(ILocalizedTextService textService)
|
||||
{
|
||||
CreateMap<Core.Models.Section, Section>()
|
||||
.ForMember(dest => dest.RoutePath, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.Icon, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.Name, opt => opt.MapFrom(src => textService.Localize("sections/" + src.Alias, (IDictionary<string, string>)null)))
|
||||
.ReverseMap(); //backwards too!
|
||||
}
|
||||
}
|
||||
}
|
||||
21
src/Umbraco.Web/Models/Mapping/SectionModelMapper.cs
Normal file
21
src/Umbraco.Web/Models/Mapping/SectionModelMapper.cs
Normal file
@@ -0,0 +1,21 @@
|
||||
using AutoMapper;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Models.Mapping;
|
||||
using Umbraco.Web.Models.ContentEditing;
|
||||
using umbraco;
|
||||
|
||||
namespace Umbraco.Web.Models.Mapping
|
||||
{
|
||||
internal class SectionModelMapper : MapperConfiguration
|
||||
{
|
||||
public override void ConfigureMappings(IConfiguration config, ApplicationContext applicationContext)
|
||||
{
|
||||
config.CreateMap<Core.Models.Section, Section>()
|
||||
.ForMember(section => section.RoutePath, x => x.Ignore())
|
||||
.ForMember(
|
||||
dto => dto.Name,
|
||||
expression => expression.MapFrom(section => ui.Text("sections", section.Alias)))
|
||||
.ReverseMap(); //backwards too!
|
||||
}
|
||||
}
|
||||
}
|
||||
15
src/Umbraco.Web/Models/Mapping/TabModelMapper.cs
Normal file
15
src/Umbraco.Web/Models/Mapping/TabModelMapper.cs
Normal file
@@ -0,0 +1,15 @@
|
||||
using AutoMapper;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Models.Mapping;
|
||||
|
||||
namespace Umbraco.Web.Models.Mapping
|
||||
{
|
||||
internal class TabModelMapper : MapperConfiguration
|
||||
{
|
||||
public override void ConfigureMappings(IConfiguration config, ApplicationContext applicationContext)
|
||||
{
|
||||
config.CreateMap<ITag, TagModel>();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,203 +1,310 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using AutoMapper;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Services;
|
||||
using Umbraco.Web.Models.ContentEditing;
|
||||
using Umbraco.Web.Composing;
|
||||
|
||||
namespace Umbraco.Web.Models.Mapping
|
||||
{
|
||||
internal abstract class TabsAndPropertiesResolver
|
||||
{
|
||||
protected ILocalizedTextService LocalizedTextService { get; }
|
||||
protected IEnumerable<string> IgnoreProperties { get; set; }
|
||||
|
||||
protected TabsAndPropertiesResolver(ILocalizedTextService localizedTextService)
|
||||
{
|
||||
LocalizedTextService = localizedTextService ?? throw new ArgumentNullException(nameof(localizedTextService));
|
||||
IgnoreProperties = new List<string>();
|
||||
}
|
||||
|
||||
protected TabsAndPropertiesResolver(ILocalizedTextService localizedTextService, IEnumerable<string> ignoreProperties)
|
||||
: this(localizedTextService)
|
||||
{
|
||||
IgnoreProperties = ignoreProperties ?? throw new ArgumentNullException(nameof(ignoreProperties));
|
||||
}
|
||||
|
||||
//TODO: This should deserialize to ListViewConfiguration
|
||||
private static int GetTabNumberFromConfig(IDictionary<string, object> listViewConfig)
|
||||
{
|
||||
if (!listViewConfig.TryGetValue("displayAtTabNumber", out var displayTabNum))
|
||||
return -1;
|
||||
switch (displayTabNum)
|
||||
{
|
||||
case int i:
|
||||
return i;
|
||||
case string s when int.TryParse(s, out var parsed):
|
||||
return parsed;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a collection of custom generic properties that exist on the generic properties tab
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
protected virtual IEnumerable<ContentPropertyDisplay> GetCustomGenericProperties(IContentBase content)
|
||||
{
|
||||
return Enumerable.Empty<ContentPropertyDisplay>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Maps properties on to the generic properties tab
|
||||
/// </summary>
|
||||
/// <param name="content"></param>
|
||||
/// <param name="tabs"></param>
|
||||
/// <param name="context"></param>
|
||||
/// <remarks>
|
||||
/// The generic properties tab is responsible for
|
||||
/// setting up the properties such as Created date, updated date, template selected, etc...
|
||||
/// </remarks>
|
||||
protected virtual void MapGenericProperties(IContentBase content, List<Tab<ContentPropertyDisplay>> tabs, ResolutionContext context)
|
||||
{
|
||||
// add the generic properties tab, for properties that don't belong to a tab
|
||||
// get the properties, map and translate them, then add the tab
|
||||
var noGroupProperties = content.GetNonGroupedProperties()
|
||||
.Where(x => IgnoreProperties.Contains(x.Alias) == false) // skip ignored
|
||||
.ToList();
|
||||
var genericproperties = MapProperties(content, noGroupProperties, context);
|
||||
|
||||
tabs.Add(new Tab<ContentPropertyDisplay>
|
||||
{
|
||||
Id = 0,
|
||||
Label = LocalizedTextService.Localize("general/properties"),
|
||||
Alias = "Generic properties",
|
||||
Properties = genericproperties
|
||||
});
|
||||
|
||||
var genericProps = tabs.Single(x => x.Id == 0);
|
||||
|
||||
//store the current props to append to the newly inserted ones
|
||||
var currProps = genericProps.Properties.ToArray();
|
||||
|
||||
var contentProps = new List<ContentPropertyDisplay>();
|
||||
|
||||
var customProperties = GetCustomGenericProperties(content);
|
||||
if (customProperties != null)
|
||||
{
|
||||
//add the custom ones
|
||||
contentProps.AddRange(customProperties);
|
||||
}
|
||||
|
||||
//now add the user props
|
||||
contentProps.AddRange(currProps);
|
||||
|
||||
//re-assign
|
||||
genericProps.Properties = contentProps;
|
||||
|
||||
//Show or hide properties tab based on wether it has or not any properties
|
||||
if (genericProps.Properties.Any() == false)
|
||||
{
|
||||
//loop throug the tabs, remove the one with the id of zero and exit the loop
|
||||
for (var i = 0; i < tabs.Count; i++)
|
||||
{
|
||||
if (tabs[i].Id != 0) continue;
|
||||
tabs.RemoveAt(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Maps a list of <see cref="Property"/> to a list of <see cref="ContentPropertyDisplay"/>
|
||||
/// </summary>
|
||||
/// <param name="content"></param>
|
||||
/// <param name="properties"></param>
|
||||
/// <param name="context"></param>
|
||||
/// <returns></returns>
|
||||
protected virtual List<ContentPropertyDisplay> MapProperties(IContentBase content, List<Property> properties, ResolutionContext context)
|
||||
{
|
||||
//we need to map this way to pass the context through, I don't like it but we'll see what AutoMapper says: https://github.com/AutoMapper/AutoMapper/issues/2588
|
||||
var result = context.Mapper.Map<IEnumerable<Property>, IEnumerable<ContentPropertyDisplay>>(
|
||||
properties.OrderBy(prop => prop.PropertyType.SortOrder),
|
||||
null,
|
||||
context)
|
||||
.ToList();
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the tabs collection with properties assigned for display models
|
||||
/// </summary>
|
||||
internal class TabsAndPropertiesResolver<TSource, TDestination> : TabsAndPropertiesResolver, IValueResolver<TSource, TDestination, IEnumerable<Tab<ContentPropertyDisplay>>>
|
||||
where TSource : IContentBase
|
||||
{
|
||||
public TabsAndPropertiesResolver(ILocalizedTextService localizedTextService)
|
||||
: base(localizedTextService)
|
||||
{
|
||||
}
|
||||
|
||||
public TabsAndPropertiesResolver(ILocalizedTextService localizedTextService, IEnumerable<string> ignoreProperties)
|
||||
: base(localizedTextService, ignoreProperties)
|
||||
{
|
||||
}
|
||||
|
||||
public virtual IEnumerable<Tab<ContentPropertyDisplay>> Resolve(TSource source, TDestination destination, IEnumerable<Tab<ContentPropertyDisplay>> destMember, ResolutionContext context)
|
||||
{
|
||||
var tabs = new List<Tab<ContentPropertyDisplay>>();
|
||||
|
||||
// add the tabs, for properties that belong to a tab
|
||||
// need to aggregate the tabs, as content.PropertyGroups contains all the composition tabs,
|
||||
// and there might be duplicates (content does not work like contentType and there is no
|
||||
// content.CompositionPropertyGroups).
|
||||
var groupsGroupsByName = source.PropertyGroups.OrderBy(x => x.SortOrder).GroupBy(x => x.Name);
|
||||
foreach (var groupsByName in groupsGroupsByName)
|
||||
{
|
||||
var properties = new List<Property>();
|
||||
|
||||
// merge properties for groups with the same name
|
||||
foreach (var group in groupsByName)
|
||||
{
|
||||
var groupProperties = source.GetPropertiesForGroup(group)
|
||||
.Where(x => IgnoreProperties.Contains(x.Alias) == false); // skip ignored
|
||||
|
||||
properties.AddRange(groupProperties);
|
||||
}
|
||||
|
||||
if (properties.Count == 0)
|
||||
continue;
|
||||
|
||||
//map the properties
|
||||
var mappedProperties = MapProperties(source, properties, context);
|
||||
|
||||
// add the tab
|
||||
// we need to pick an identifier... there is no "right" way...
|
||||
var g = groupsByName.FirstOrDefault(x => x.Id == source.ContentTypeId) // try local
|
||||
?? groupsByName.First(); // else pick one randomly
|
||||
var groupId = g.Id;
|
||||
var groupName = groupsByName.Key;
|
||||
tabs.Add(new Tab<ContentPropertyDisplay>
|
||||
{
|
||||
Id = groupId,
|
||||
Alias = groupName,
|
||||
Label = LocalizedTextService.UmbracoDictionaryTranslate(groupName),
|
||||
Properties = mappedProperties,
|
||||
IsActive = false
|
||||
});
|
||||
}
|
||||
|
||||
MapGenericProperties(source, tabs, context);
|
||||
|
||||
// activate the first tab, if any
|
||||
if (tabs.Count > 0)
|
||||
tabs[0].IsActive = true;
|
||||
|
||||
return tabs;
|
||||
}
|
||||
}
|
||||
}
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
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;
|
||||
|
||||
namespace Umbraco.Web.Models.Mapping
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates the tabs collection with properties assigned for display models
|
||||
/// </summary>
|
||||
internal class TabsAndPropertiesResolver<TSource> : IValueResolver
|
||||
where TSource : IContentBase
|
||||
{
|
||||
private readonly ILocalizedTextService _localizedTextService;
|
||||
protected IEnumerable<string> IgnoreProperties { get; set; }
|
||||
|
||||
public TabsAndPropertiesResolver(ILocalizedTextService localizedTextService)
|
||||
{
|
||||
if (localizedTextService == null) throw new ArgumentNullException("localizedTextService");
|
||||
_localizedTextService = localizedTextService;
|
||||
IgnoreProperties = new List<string>();
|
||||
}
|
||||
|
||||
public TabsAndPropertiesResolver(ILocalizedTextService localizedTextService, IEnumerable<string> ignoreProperties)
|
||||
: this(localizedTextService)
|
||||
{
|
||||
if (ignoreProperties == null) throw new ArgumentNullException("ignoreProperties");
|
||||
IgnoreProperties = ignoreProperties;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Implements the <see cref="IValueResolver"/>
|
||||
/// </summary>
|
||||
/// <param name="source"></param>
|
||||
/// <returns></returns>
|
||||
public ResolutionResult Resolve(ResolutionResult source)
|
||||
{
|
||||
if (source.Value != null && (source.Value is TSource) == false)
|
||||
throw new AutoMapperMappingException(string.Format("Value supplied is of type {0} but expected {1}.\nChange the value resolver source type, or redirect the source value supplied to the value resolver using FromMember.", new object[]
|
||||
{
|
||||
source.Value.GetType(),
|
||||
typeof (TSource)
|
||||
}));
|
||||
return source.New(
|
||||
//perform the mapping with the current umbraco context
|
||||
ResolveCore(source.Context.GetUmbracoContext(), (TSource)source.Value), typeof(List<Tab<ContentPropertyDisplay>>));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the container (listview) tab to the document
|
||||
/// </summary>
|
||||
/// <typeparam name="TPersisted"></typeparam>
|
||||
/// <param name="display"></param>
|
||||
/// <param name="entityType">This must be either 'content' or 'media'</param>
|
||||
/// <param name="dataTypeService"></param>
|
||||
/// <param name="localizedTextService"></param>
|
||||
internal static void AddListView<TPersisted>(TabbedContentItem<ContentPropertyDisplay, TPersisted> display, string entityType, IDataTypeService dataTypeService, ILocalizedTextService localizedTextService)
|
||||
where TPersisted : IContentBase
|
||||
{
|
||||
int dtdId;
|
||||
var customDtdName = Constants.Conventions.DataTypes.ListViewPrefix + display.ContentTypeAlias;
|
||||
switch (entityType)
|
||||
{
|
||||
case "content":
|
||||
dtdId = Constants.System.DefaultContentListViewDataTypeId;
|
||||
|
||||
break;
|
||||
case "media":
|
||||
dtdId = Constants.System.DefaultMediaListViewDataTypeId;
|
||||
break;
|
||||
case "member":
|
||||
dtdId = Constants.System.DefaultMembersListViewDataTypeId;
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException("entityType does not match a required value");
|
||||
}
|
||||
|
||||
//first try to get the custom one if there is one
|
||||
var dt = dataTypeService.GetDataTypeDefinitionByName(customDtdName)
|
||||
?? dataTypeService.GetDataTypeDefinitionById(dtdId);
|
||||
|
||||
if (dt == null)
|
||||
{
|
||||
throw new InvalidOperationException("No list view data type was found for this document type, ensure that the default list view data types exists and/or that your custom list view data type exists");
|
||||
}
|
||||
|
||||
var preVals = dataTypeService.GetPreValuesCollectionByDataTypeId(dt.Id);
|
||||
|
||||
var editor = PropertyEditorResolver.Current.GetByAlias(dt.PropertyEditorAlias);
|
||||
if (editor == null)
|
||||
{
|
||||
throw new NullReferenceException("The property editor with alias " + dt.PropertyEditorAlias + " does not exist");
|
||||
}
|
||||
|
||||
var listViewTab = new Tab<ContentPropertyDisplay>();
|
||||
listViewTab.Alias = Constants.Conventions.PropertyGroups.ListViewGroupName;
|
||||
listViewTab.Label = localizedTextService.Localize("content/childItems");
|
||||
listViewTab.Id = display.Tabs.Count() + 1;
|
||||
listViewTab.IsActive = true;
|
||||
|
||||
var listViewConfig = editor.PreValueEditor.ConvertDbToEditor(editor.DefaultPreValues, preVals);
|
||||
//add the entity type to the config
|
||||
listViewConfig["entityType"] = entityType;
|
||||
|
||||
//Override Tab Label if tabName is provided
|
||||
if (listViewConfig.ContainsKey("tabName"))
|
||||
{
|
||||
var configTabName = listViewConfig["tabName"];
|
||||
if (configTabName != null && string.IsNullOrWhiteSpace(configTabName.ToString()) == false)
|
||||
listViewTab.Label = configTabName.ToString();
|
||||
}
|
||||
|
||||
var listViewProperties = new List<ContentPropertyDisplay>();
|
||||
listViewProperties.Add(new ContentPropertyDisplay
|
||||
{
|
||||
Alias = string.Format("{0}containerView", Constants.PropertyEditors.InternalGenericPropertiesPrefix),
|
||||
Label = "",
|
||||
Value = null,
|
||||
View = editor.ValueEditor.View,
|
||||
HideLabel = true,
|
||||
Config = listViewConfig
|
||||
});
|
||||
listViewTab.Properties = listViewProperties;
|
||||
|
||||
SetChildItemsTabPosition(display, listViewConfig, listViewTab);
|
||||
}
|
||||
|
||||
private static void SetChildItemsTabPosition<TPersisted>(TabbedContentItem<ContentPropertyDisplay, TPersisted> display,
|
||||
IDictionary<string, object> listViewConfig,
|
||||
Tab<ContentPropertyDisplay> listViewTab)
|
||||
where TPersisted : IContentBase
|
||||
{
|
||||
// Find position of tab from config
|
||||
var tabIndexForChildItems = 0;
|
||||
if (listViewConfig["displayAtTabNumber"] != null && int.TryParse((string)listViewConfig["displayAtTabNumber"], out tabIndexForChildItems))
|
||||
{
|
||||
// Tab position is recorded 1-based but we insert into collection 0-based
|
||||
tabIndexForChildItems--;
|
||||
|
||||
// Ensure within bounds
|
||||
if (tabIndexForChildItems < 0)
|
||||
{
|
||||
tabIndexForChildItems = 0;
|
||||
}
|
||||
|
||||
if (tabIndexForChildItems > display.Tabs.Count())
|
||||
{
|
||||
tabIndexForChildItems = display.Tabs.Count();
|
||||
}
|
||||
}
|
||||
|
||||
// Recreate tab list with child items tab at configured position
|
||||
var tabs = new List<Tab<ContentPropertyDisplay>>();
|
||||
tabs.AddRange(display.Tabs);
|
||||
tabs.Insert(tabIndexForChildItems, listViewTab);
|
||||
display.Tabs = tabs;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create the list of tabs for the <see cref="IContentBase"/>
|
||||
/// </summary>
|
||||
/// <param name="umbracoContext"></param>
|
||||
/// <param name="content">Source value</param>
|
||||
/// <returns>Destination</returns>
|
||||
protected virtual List<Tab<ContentPropertyDisplay>> ResolveCore(UmbracoContext umbracoContext, TSource content)
|
||||
{
|
||||
var tabs = new List<Tab<ContentPropertyDisplay>>();
|
||||
|
||||
// add the tabs, for properties that belong to a tab
|
||||
// need to aggregate the tabs, as content.PropertyGroups contains all the composition tabs,
|
||||
// and there might be duplicates (content does not work like contentType and there is no
|
||||
// content.CompositionPropertyGroups).
|
||||
var groupsGroupsByName = content.PropertyGroups.OrderBy(x => x.SortOrder).GroupBy(x => x.Name);
|
||||
foreach (var groupsByName in groupsGroupsByName)
|
||||
{
|
||||
var properties = new List<Property>();
|
||||
|
||||
// merge properties for groups with the same name
|
||||
foreach (var group in groupsByName)
|
||||
{
|
||||
var groupProperties = content.GetPropertiesForGroup(group)
|
||||
.Where(x => IgnoreProperties.Contains(x.Alias) == false); // skip ignored
|
||||
|
||||
properties.AddRange(groupProperties);
|
||||
}
|
||||
|
||||
if (properties.Count == 0)
|
||||
continue;
|
||||
|
||||
//map the properties
|
||||
var mappedProperties = MapProperties(umbracoContext, content, properties);
|
||||
|
||||
// add the tab
|
||||
// we need to pick an identifier... there is no "right" way...
|
||||
var g = groupsByName.FirstOrDefault(x => x.Id == content.ContentTypeId) // try local
|
||||
?? groupsByName.First(); // else pick one randomly
|
||||
var groupId = g.Id;
|
||||
var groupName = groupsByName.Key;
|
||||
tabs.Add(new Tab<ContentPropertyDisplay>
|
||||
{
|
||||
Id = groupId,
|
||||
Alias = groupName,
|
||||
Label = _localizedTextService.UmbracoDictionaryTranslate(groupName),
|
||||
Properties = mappedProperties,
|
||||
IsActive = false
|
||||
});
|
||||
}
|
||||
|
||||
MapGenericProperties(umbracoContext, content, tabs);
|
||||
|
||||
// activate the first tab
|
||||
if (tabs.Count > 0)
|
||||
tabs[0].IsActive = true;
|
||||
|
||||
return tabs;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a collection of custom generic properties that exist on the generic properties tab
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
protected virtual IEnumerable<ContentPropertyDisplay> GetCustomGenericProperties(IContentBase content)
|
||||
{
|
||||
return Enumerable.Empty<ContentPropertyDisplay>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Maps properties on to the generic properties tab
|
||||
/// </summary>
|
||||
/// <param name="umbracoContext"></param>
|
||||
/// <param name="content"></param>
|
||||
/// <param name="tabs"></param>
|
||||
/// <remarks>
|
||||
/// The generic properties tab is responsible for
|
||||
/// setting up the properties such as Created date, updated date, template selected, etc...
|
||||
/// </remarks>
|
||||
protected virtual void MapGenericProperties(UmbracoContext umbracoContext, IContentBase content, List<Tab<ContentPropertyDisplay>> tabs)
|
||||
{
|
||||
// add the generic properties tab, for properties that don't belong to a tab
|
||||
// get the properties, map and translate them, then add the tab
|
||||
var noGroupProperties = content.GetNonGroupedProperties()
|
||||
.Where(x => IgnoreProperties.Contains(x.Alias) == false) // skip ignored
|
||||
.ToList();
|
||||
var genericproperties = MapProperties(umbracoContext, content, noGroupProperties);
|
||||
|
||||
tabs.Add(new Tab<ContentPropertyDisplay>
|
||||
{
|
||||
Id = 0,
|
||||
Label = _localizedTextService.Localize("general/properties"),
|
||||
Alias = "Generic properties",
|
||||
Properties = genericproperties
|
||||
});
|
||||
|
||||
var genericProps = tabs.Single(x => x.Id == 0);
|
||||
|
||||
//store the current props to append to the newly inserted ones
|
||||
var currProps = genericProps.Properties.ToArray();
|
||||
|
||||
var contentProps = new List<ContentPropertyDisplay>();
|
||||
|
||||
var customProperties = GetCustomGenericProperties(content);
|
||||
if (customProperties != null)
|
||||
{
|
||||
//add the custom ones
|
||||
contentProps.AddRange(customProperties);
|
||||
}
|
||||
|
||||
//now add the user props
|
||||
contentProps.AddRange(currProps);
|
||||
|
||||
//re-assign
|
||||
genericProps.Properties = contentProps;
|
||||
|
||||
//Show or hide properties tab based on wether it has or not any properties
|
||||
if (genericProps.Properties.Any() == false)
|
||||
{
|
||||
//loop throug the tabs, remove the one with the id of zero and exit the loop
|
||||
for (var i = 0; i < tabs.Count; i++)
|
||||
{
|
||||
if (tabs[i].Id != 0) continue;
|
||||
tabs.RemoveAt(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Maps a list of <see cref="Property"/> to a list of <see cref="ContentPropertyDisplay"/>
|
||||
/// </summary>
|
||||
/// <param name="umbracoContext"></param>
|
||||
/// <param name="content"></param>
|
||||
/// <param name="properties"></param>
|
||||
/// <returns></returns>
|
||||
protected virtual List<ContentPropertyDisplay> MapProperties(UmbracoContext umbracoContext, IContentBase content, List<Property> properties)
|
||||
{
|
||||
var result = Mapper.Map<IEnumerable<Property>, IEnumerable<ContentPropertyDisplay>>(
|
||||
// Sort properties so items from different compositions appear in correct order (see U4-9298). Map sorted properties.
|
||||
properties.OrderBy(prop => prop.PropertyType.SortOrder))
|
||||
.ToList();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
using AutoMapper;
|
||||
using Umbraco.Core.Models;
|
||||
|
||||
namespace Umbraco.Web.Models.Mapping
|
||||
{
|
||||
internal class TagMapperProfile : Profile
|
||||
{
|
||||
public TagMapperProfile()
|
||||
{
|
||||
CreateMap<ITag, TagModel>();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
using AutoMapper;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Web.Models.ContentEditing;
|
||||
|
||||
namespace Umbraco.Web.Models.Mapping
|
||||
{
|
||||
internal class TemplateMapperProfile : Profile
|
||||
{
|
||||
public TemplateMapperProfile()
|
||||
{
|
||||
CreateMap<ITemplate, TemplateDisplay>()
|
||||
.ForMember(dest => dest.Notifications, opt => opt.Ignore());
|
||||
|
||||
CreateMap<TemplateDisplay, Template>()
|
||||
.IgnoreEntityCommonProperties()
|
||||
.ForMember(dest => dest.Path, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.VirtualPath, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.Path, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.MasterTemplateId, opt => opt.Ignore()) // ok, assigned when creating the template
|
||||
.ForMember(dest => dest.IsMasterTemplate, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.HasIdentity, opt => opt.Ignore());
|
||||
}
|
||||
}
|
||||
}
|
||||
26
src/Umbraco.Web/Models/Mapping/TemplateModelMapper.cs
Normal file
26
src/Umbraco.Web/Models/Mapping/TemplateModelMapper.cs
Normal file
@@ -0,0 +1,26 @@
|
||||
using AutoMapper;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Models.Mapping;
|
||||
using Umbraco.Web.Models.ContentEditing;
|
||||
|
||||
namespace Umbraco.Web.Models.Mapping
|
||||
{
|
||||
internal class TemplateModelMapper : MapperConfiguration
|
||||
{
|
||||
public override void ConfigureMappings(IConfiguration config, ApplicationContext applicationContext)
|
||||
{
|
||||
config.CreateMap<ITemplate, TemplateDisplay>()
|
||||
.ForMember(x => x.Notifications, exp => exp.Ignore());
|
||||
|
||||
config.CreateMap<TemplateDisplay, Template>()
|
||||
.IgnoreDeletableEntityCommonProperties()
|
||||
.ForMember(x => x.Path, exp => exp.Ignore())
|
||||
.ForMember(x => x.VirtualPath, exp => exp.Ignore())
|
||||
.ForMember(x => x.Path, exp => exp.Ignore())
|
||||
.ForMember(x => x.MasterTemplateId, exp => exp.Ignore()) // ok, assigned when creating the template
|
||||
.ForMember(x => x.IsMasterTemplate, exp => exp.Ignore())
|
||||
.ForMember(x => x.HasIdentity, exp => exp.Ignore());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,53 +1,55 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using AutoMapper;
|
||||
using umbraco.interfaces;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.CodeAnnotations;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Models.Membership;
|
||||
using Umbraco.Core.Services;
|
||||
using Umbraco.Web.Actions;
|
||||
using Umbraco.Web.Models.ContentEditing;
|
||||
|
||||
|
||||
namespace Umbraco.Web.Models.Mapping
|
||||
{
|
||||
/// <summary>
|
||||
/// Converts an IUserGroup instance into a dictionary of permissions by category
|
||||
/// </summary>
|
||||
internal class UserGroupDefaultPermissionsResolver
|
||||
internal class UserGroupDefaultPermissionsResolver : ValueResolver<IUserGroup, IDictionary<string, IEnumerable<Permission>>>
|
||||
{
|
||||
private readonly ILocalizedTextService _textService;
|
||||
private readonly ActionCollection _actions;
|
||||
|
||||
public UserGroupDefaultPermissionsResolver(ILocalizedTextService textService, ActionCollection actions)
|
||||
public UserGroupDefaultPermissionsResolver(ILocalizedTextService textService)
|
||||
{
|
||||
_actions = actions;
|
||||
_textService = textService ?? throw new ArgumentNullException(nameof(textService));
|
||||
if (textService == null) throw new ArgumentNullException("textService");
|
||||
_textService = textService;
|
||||
}
|
||||
|
||||
public IDictionary<string, IEnumerable<Permission>> Resolve(IUserGroup source)
|
||||
protected override IDictionary<string, IEnumerable<Permission>> ResolveCore(IUserGroup source)
|
||||
{
|
||||
return _actions
|
||||
return ActionsResolver.Current.Actions
|
||||
.Where(x => x.CanBePermissionAssigned)
|
||||
.Select(x => GetPermission(x, source))
|
||||
.GroupBy(x => x.Category)
|
||||
.ToDictionary(x => x.Key, x => (IEnumerable<Permission>) x.ToArray());
|
||||
.ToDictionary(x => x.Key, x => (IEnumerable<Permission>)x.ToArray());
|
||||
}
|
||||
|
||||
private Permission GetPermission(IAction action, IUserGroup source)
|
||||
{
|
||||
var result = new Permission();
|
||||
|
||||
result.Category = action.Category.IsNullOrWhiteSpace()
|
||||
? _textService.Localize($"actionCategories/{Constants.Conventions.PermissionCategories.OtherCategory}")
|
||||
: _textService.Localize($"actionCategories/{action.Category}");
|
||||
result.Name = _textService.Localize($"actions/{action.Alias}");
|
||||
result.Description = _textService.Localize($"actionDescriptions/{action.Alias}");
|
||||
var attribute = action.GetType().GetCustomAttribute<ActionMetadataAttribute>(false);
|
||||
result.Category = attribute == null
|
||||
? _textService.Localize(string.Format("actionCategories/{0}", Constants.Conventions.PermissionCategories.OtherCategory))
|
||||
: _textService.Localize(string.Format("actionCategories/{0}", attribute.Category));
|
||||
result.Name = attribute == null || attribute.Name.IsNullOrWhiteSpace()
|
||||
? _textService.Localize(string.Format("actions/{0}", action.Alias))
|
||||
: attribute.Name;
|
||||
result.Description = _textService.Localize(String.Format("actionDescriptions/{0}", action.Alias));
|
||||
result.Icon = action.Icon;
|
||||
result.Checked = source.Permissions != null && source.Permissions.Contains(action.Letter.ToString(CultureInfo.InvariantCulture));
|
||||
result.PermissionCode = action.Letter.ToString(CultureInfo.InvariantCulture);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,428 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using AutoMapper;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Cache;
|
||||
using Umbraco.Core.Configuration;
|
||||
using Umbraco.Core.Models.Membership;
|
||||
using Umbraco.Web.Models.ContentEditing;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Models.Entities;
|
||||
using Umbraco.Core.Security;
|
||||
using Umbraco.Core.Services;
|
||||
using Umbraco.Web.Actions;
|
||||
|
||||
|
||||
namespace Umbraco.Web.Models.Mapping
|
||||
{
|
||||
internal class UserMapperProfile : Profile
|
||||
{
|
||||
private static string GetContentTypeIcon(EntitySlim entity)
|
||||
=> entity is ContentEntitySlim contentEntity ? contentEntity.ContentTypeIcon : null;
|
||||
|
||||
public UserMapperProfile(ILocalizedTextService textService, IUserService userService, IEntityService entityService, ISectionService sectionService,
|
||||
IRuntimeCacheProvider runtimeCache, ActionCollection actions, IGlobalSettings globalSettings)
|
||||
{
|
||||
var userGroupDefaultPermissionsResolver = new UserGroupDefaultPermissionsResolver(textService, actions);
|
||||
|
||||
CreateMap<UserGroupSave, IUserGroup>()
|
||||
.ConstructUsing(save => new UserGroup { CreateDate = DateTime.UtcNow })
|
||||
.IgnoreEntityCommonProperties()
|
||||
.ForMember(dest => dest.Id, opt => opt.Condition(source => GetIntId(source.Id) > 0))
|
||||
.ForMember(dest => dest.Id, opt => opt.MapFrom(source => GetIntId(source.Id)))
|
||||
.ForMember(dest => dest.Permissions, opt => opt.MapFrom(source => source.DefaultPermissions))
|
||||
.AfterMap((save, userGroup) =>
|
||||
{
|
||||
userGroup.ClearAllowedSections();
|
||||
foreach (var section in save.Sections)
|
||||
{
|
||||
userGroup.AddAllowedSection(section);
|
||||
}
|
||||
});
|
||||
|
||||
//Used for merging existing UserSave to an existing IUser instance - this will not create an IUser instance!
|
||||
CreateMap<UserSave, IUser>()
|
||||
.IgnoreEntityCommonProperties()
|
||||
.ForMember(dest => dest.Id, opt => opt.Condition(src => GetIntId(src.Id) > 0))
|
||||
.ForMember(detail => detail.TourData, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.SessionTimeout, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.EmailConfirmedDate, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.InvitedDate, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.SecurityStamp, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.Avatar, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.ProviderUserKey, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.RawPasswordValue, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.RawPasswordAnswerValue, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.PasswordQuestion, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.Comments, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.IsApproved, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.IsLockedOut, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.LastLoginDate, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.LastPasswordChangeDate, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.LastLockoutDate, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.FailedPasswordAttempts, opt => opt.Ignore())
|
||||
.ForMember(user => user.Language, opt => opt.MapFrom(save => save.Culture))
|
||||
.AfterMap((save, user) =>
|
||||
{
|
||||
user.ClearGroups();
|
||||
var foundGroups = userService.GetUserGroupsByAlias(save.UserGroups.ToArray());
|
||||
foreach (var group in foundGroups)
|
||||
{
|
||||
user.AddGroup(group.ToReadOnlyGroup());
|
||||
}
|
||||
});
|
||||
|
||||
CreateMap<UserInvite, IUser>()
|
||||
.IgnoreEntityCommonProperties()
|
||||
.ForMember(dest => dest.Id, opt => opt.Ignore())
|
||||
.ForMember(detail => detail.TourData, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.StartContentIds, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.StartMediaIds, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.Language, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.Username, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.PasswordQuestion, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.SessionTimeout, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.EmailConfirmedDate, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.InvitedDate, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.SecurityStamp, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.Avatar, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.ProviderUserKey, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.RawPasswordValue, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.RawPasswordAnswerValue, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.Comments, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.IsApproved, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.IsLockedOut, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.LastLoginDate, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.LastPasswordChangeDate, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.LastLockoutDate, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.FailedPasswordAttempts, opt => opt.Ignore())
|
||||
//all invited users will not be approved, completing the invite will approve the user
|
||||
.ForMember(user => user.IsApproved, opt => opt.UseValue(false))
|
||||
.AfterMap((invite, user) =>
|
||||
{
|
||||
user.ClearGroups();
|
||||
var foundGroups = userService.GetUserGroupsByAlias(invite.UserGroups.ToArray());
|
||||
foreach (var group in foundGroups)
|
||||
{
|
||||
user.AddGroup(group.ToReadOnlyGroup());
|
||||
}
|
||||
});
|
||||
|
||||
CreateMap<IReadOnlyUserGroup, UserGroupBasic>()
|
||||
.ForMember(dest => dest.ContentStartNode, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.UserCount, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.MediaStartNode, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.Key, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.Sections, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.Notifications, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.Udi, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.Trashed, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.ParentId, opt => opt.UseValue(-1))
|
||||
.ForMember(dest => dest.Path, opt => opt.MapFrom(userGroup => "-1," + userGroup.Id))
|
||||
.ForMember(dest => dest.AdditionalData, opt => opt.Ignore())
|
||||
.AfterMap((group, display) =>
|
||||
{
|
||||
MapUserGroupBasic(sectionService, entityService, textService, group, display);
|
||||
});
|
||||
|
||||
CreateMap<IUserGroup, UserGroupBasic>()
|
||||
.ForMember(dest => dest.ContentStartNode, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.MediaStartNode, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.Sections, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.Notifications, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.Udi, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.Trashed, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.ParentId, opt => opt.UseValue(-1))
|
||||
.ForMember(dest => dest.Path, opt => opt.MapFrom(userGroup => "-1," + userGroup.Id))
|
||||
.ForMember(dest => dest.AdditionalData, opt => opt.Ignore())
|
||||
.AfterMap((group, display) =>
|
||||
{
|
||||
MapUserGroupBasic(sectionService, entityService, textService, group, display);
|
||||
});
|
||||
|
||||
//create a map to assign a user group's default permissions to the AssignedUserGroupPermissions instance
|
||||
CreateMap<IUserGroup, AssignedUserGroupPermissions>()
|
||||
.ForMember(dest => dest.Udi, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.Trashed, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.AdditionalData, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.Id, opt => opt.MapFrom(group => group.Id))
|
||||
.ForMember(dest => dest.ParentId, opt => opt.UseValue(-1))
|
||||
.ForMember(dest => dest.Path, opt => opt.MapFrom(userGroup => "-1," + userGroup.Id))
|
||||
.ForMember(dest => dest.DefaultPermissions, opt => opt.ResolveUsing(src => userGroupDefaultPermissionsResolver.Resolve(src)))
|
||||
//these will be manually mapped and by default they are null
|
||||
.ForMember(dest => dest.AssignedPermissions, opt => opt.Ignore())
|
||||
.AfterMap((group, display) =>
|
||||
{
|
||||
if (display.Icon.IsNullOrWhiteSpace())
|
||||
{
|
||||
display.Icon = "icon-users";
|
||||
}
|
||||
});
|
||||
|
||||
CreateMap<EntitySlim, AssignedContentPermissions>()
|
||||
.ForMember(x => x.Udi, opt => opt.MapFrom(x => Udi.Create(ObjectTypes.GetUdiType(x.NodeObjectType), x.Key)))
|
||||
.ForMember(basic => basic.Icon, opt => opt.MapFrom(entity => GetContentTypeIcon(entity)))
|
||||
.ForMember(dto => dto.Trashed, opt => opt.Ignore())
|
||||
.ForMember(x => x.Alias, opt => opt.Ignore())
|
||||
.ForMember(x => x.AssignedPermissions, opt => opt.Ignore())
|
||||
.AfterMap((entity, basic) =>
|
||||
{
|
||||
if (entity.NodeObjectType == Constants.ObjectTypes.Member && basic.Icon.IsNullOrWhiteSpace())
|
||||
{
|
||||
basic.Icon = "icon-user";
|
||||
}
|
||||
});
|
||||
|
||||
CreateMap<IUserGroup, UserGroupDisplay>()
|
||||
.ForMember(dest => dest.ContentStartNode, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.MediaStartNode, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.Sections, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.Notifications, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.Udi, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.Trashed, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.ParentId, opt => opt.UseValue(-1))
|
||||
.ForMember(dest => dest.Path, opt => opt.MapFrom(userGroup => "-1," + userGroup.Id))
|
||||
.ForMember(dest => dest.AdditionalData, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.Users, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.DefaultPermissions, opt => opt.ResolveUsing(src => userGroupDefaultPermissionsResolver.Resolve(src)))
|
||||
.ForMember(dest => dest.AssignedPermissions, opt => opt.Ignore())
|
||||
.AfterMap((group, display) =>
|
||||
{
|
||||
MapUserGroupBasic(sectionService, entityService, textService, group, display);
|
||||
|
||||
//Important! Currently we are never mapping to multiple UserGroupDisplay objects but if we start doing that
|
||||
// this will cause an N+1 and we'll need to change how this works.
|
||||
var users = userService.GetAllInGroup(group.Id);
|
||||
display.Users = Mapper.Map<IEnumerable<UserBasic>>(users);
|
||||
|
||||
//Deal with assigned permissions:
|
||||
|
||||
var allContentPermissions = userService.GetPermissions(@group, true)
|
||||
.ToDictionary(x => x.EntityId, x => x);
|
||||
|
||||
IEntitySlim[] contentEntities;
|
||||
if (allContentPermissions.Keys.Count == 0)
|
||||
{
|
||||
contentEntities = Array.Empty<IEntitySlim>();
|
||||
}
|
||||
else
|
||||
{
|
||||
// a group can end up with way more than 2000 assigned permissions,
|
||||
// so we need to break them into groups in order to avoid breaking
|
||||
// the entity service due to too many Sql parameters.
|
||||
|
||||
var list = new List<IEntitySlim>();
|
||||
foreach (var idGroup in allContentPermissions.Keys.InGroupsOf(2000))
|
||||
list.AddRange(entityService.GetAll(UmbracoObjectTypes.Document, idGroup.ToArray()));
|
||||
contentEntities = list.ToArray();
|
||||
}
|
||||
|
||||
var allAssignedPermissions = new List<AssignedContentPermissions>();
|
||||
foreach (var entity in contentEntities)
|
||||
{
|
||||
var contentPermissions = allContentPermissions[entity.Id];
|
||||
|
||||
var assignedContentPermissions = Mapper.Map<AssignedContentPermissions>(entity);
|
||||
assignedContentPermissions.AssignedPermissions = AssignedUserGroupPermissions.ClonePermissions(display.DefaultPermissions);
|
||||
|
||||
//since there is custom permissions assigned to this node for this group, we need to clear all of the default permissions
|
||||
//and we'll re-check it if it's one of the explicitly assigned ones
|
||||
foreach (var permission in assignedContentPermissions.AssignedPermissions.SelectMany(x => x.Value))
|
||||
{
|
||||
permission.Checked = false;
|
||||
permission.Checked = contentPermissions.AssignedPermissions.Contains(permission.PermissionCode, StringComparer.InvariantCulture);
|
||||
}
|
||||
|
||||
allAssignedPermissions.Add(assignedContentPermissions);
|
||||
}
|
||||
|
||||
display.AssignedPermissions = allAssignedPermissions;
|
||||
});
|
||||
|
||||
//Important! Currently we are never mapping to multiple UserDisplay objects but if we start doing that
|
||||
// this will cause an N+1 and we'll need to change how this works.
|
||||
CreateMap<IUser, UserDisplay>()
|
||||
.ForMember(dest => dest.Avatars, opt => opt.MapFrom(user => user.GetUserAvatarUrls(runtimeCache)))
|
||||
.ForMember(dest => dest.Username, opt => opt.MapFrom(user => user.Username))
|
||||
.ForMember(dest => dest.LastLoginDate, opt => opt.MapFrom(user => user.LastLoginDate == default(DateTime) ? null : (DateTime?)user.LastLoginDate))
|
||||
.ForMember(dest => dest.UserGroups, opt => opt.MapFrom(user => user.Groups))
|
||||
.ForMember(detail => detail.Navigation, opt => opt.MapFrom(user => CreateUserEditorNavigation(textService)))
|
||||
.ForMember(
|
||||
dest => dest.CalculatedStartContentIds,
|
||||
opt => opt.MapFrom(src => GetStartNodeValues(
|
||||
src.CalculateContentStartNodeIds(entityService),
|
||||
textService, entityService, UmbracoObjectTypes.Document, "content/contentRoot")))
|
||||
.ForMember(
|
||||
dest => dest.CalculatedStartMediaIds,
|
||||
opt => opt.MapFrom(src => GetStartNodeValues(
|
||||
src.CalculateMediaStartNodeIds(entityService),
|
||||
textService, entityService, UmbracoObjectTypes.Media, "media/mediaRoot")))
|
||||
.ForMember(
|
||||
dest => dest.StartContentIds,
|
||||
opt => opt.MapFrom(src => GetStartNodeValues(
|
||||
src.StartContentIds.ToArray(),
|
||||
textService, entityService, UmbracoObjectTypes.Document, "content/contentRoot")))
|
||||
.ForMember(
|
||||
dest => dest.StartMediaIds,
|
||||
opt => opt.MapFrom(src => GetStartNodeValues(
|
||||
src.StartMediaIds.ToArray(),
|
||||
textService, entityService, UmbracoObjectTypes.Media, "media/mediaRoot")))
|
||||
.ForMember(dest => dest.Culture, opt => opt.MapFrom(user => user.GetUserCulture(textService, globalSettings)))
|
||||
.ForMember(
|
||||
dest => dest.AvailableCultures,
|
||||
opt => opt.MapFrom(user => textService.GetSupportedCultures().ToDictionary(x => x.Name, x => x.DisplayName)))
|
||||
.ForMember(
|
||||
dest => dest.EmailHash,
|
||||
opt => opt.MapFrom(user => user.Email.ToLowerInvariant().Trim().GenerateHash()))
|
||||
.ForMember(dest => dest.ParentId, opt => opt.UseValue(-1))
|
||||
.ForMember(dest => dest.Path, opt => opt.MapFrom(user => "-1," + user.Id))
|
||||
.ForMember(dest => dest.Notifications, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.Udi, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.Icon, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.IsCurrentUser, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.Trashed, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.ResetPasswordValue, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.Alias, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.Trashed, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.AdditionalData, opt => opt.Ignore());
|
||||
|
||||
CreateMap<IUser, UserBasic>()
|
||||
//Loading in the user avatar's requires an external request if they don't have a local file avatar, this means that initial load of paging may incur a cost
|
||||
//Alternatively, if this is annoying the back office UI would need to be updated to request the avatars for the list of users separately so it doesn't look
|
||||
//like the load time is waiting.
|
||||
.ForMember(detail =>
|
||||
detail.Avatars,
|
||||
opt => opt.MapFrom(user => user.GetUserAvatarUrls(runtimeCache)))
|
||||
.ForMember(dest => dest.Username, opt => opt.MapFrom(user => user.Username))
|
||||
.ForMember(dest => dest.UserGroups, opt => opt.MapFrom(user => user.Groups))
|
||||
.ForMember(dest => dest.LastLoginDate, opt => opt.MapFrom(user => user.LastLoginDate == default(DateTime) ? null : (DateTime?)user.LastLoginDate))
|
||||
.ForMember(dest => dest.Culture, opt => opt.MapFrom(user => user.GetUserCulture(textService, globalSettings)))
|
||||
.ForMember(
|
||||
dest => dest.EmailHash,
|
||||
opt => opt.MapFrom(user => user.Email.ToLowerInvariant().Trim().ToMd5()))
|
||||
.ForMember(dest => dest.ParentId, opt => opt.UseValue(-1))
|
||||
.ForMember(dest => dest.Path, opt => opt.MapFrom(user => "-1," + user.Id))
|
||||
.ForMember(dest => dest.Notifications, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.IsCurrentUser, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.Udi, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.Icon, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.Trashed, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.Alias, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.Trashed, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.AdditionalData, opt => opt.Ignore());
|
||||
|
||||
CreateMap<IUser, UserDetail>()
|
||||
.ForMember(dest => dest.Avatars, opt => opt.MapFrom(user => user.GetUserAvatarUrls(runtimeCache)))
|
||||
.ForMember(dest => dest.UserId, opt => opt.MapFrom(user => GetIntId(user.Id)))
|
||||
.ForMember(dest => dest.StartContentIds, opt => opt.MapFrom(user => user.CalculateContentStartNodeIds(entityService)))
|
||||
.ForMember(dest => dest.StartMediaIds, opt => opt.MapFrom(user => user.CalculateMediaStartNodeIds(entityService)))
|
||||
.ForMember(dest => dest.Culture, opt => opt.MapFrom(user => user.GetUserCulture(textService, globalSettings)))
|
||||
.ForMember(
|
||||
dest => dest.EmailHash,
|
||||
opt => opt.MapFrom(user => user.Email.ToLowerInvariant().Trim().GenerateHash()))
|
||||
.ForMember(dest => dest.SecondsUntilTimeout, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.UserGroups, opt => opt.Ignore())
|
||||
.AfterMap((user, detail) =>
|
||||
{
|
||||
//we need to map the legacy UserType
|
||||
//the best we can do here is to return the user's first user group as a IUserType object
|
||||
//but we should attempt to return any group that is the built in ones first
|
||||
var groups = user.Groups.ToArray();
|
||||
detail.UserGroups = user.Groups.Select(x => x.Alias).ToArray();
|
||||
|
||||
});
|
||||
|
||||
CreateMap<IProfile, ContentEditing.UserProfile>()
|
||||
.ForMember(dest => dest.UserId, opt => opt.MapFrom(profile => GetIntId(profile.Id)));
|
||||
}
|
||||
|
||||
private IEnumerable<EditorNavigation> CreateUserEditorNavigation(ILocalizedTextService textService)
|
||||
{
|
||||
return new[]
|
||||
{
|
||||
new EditorNavigation
|
||||
{
|
||||
Active = true,
|
||||
Alias = "details",
|
||||
Icon = "icon-umb-users",
|
||||
Name = textService.Localize("general/user"),
|
||||
View = "views/users/views/user/details.html"
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private IEnumerable<EntityBasic> GetStartNodeValues(int[] startNodeIds,
|
||||
ILocalizedTextService textService, IEntityService entityService, UmbracoObjectTypes objectType,
|
||||
string localizedKey)
|
||||
{
|
||||
if (startNodeIds.Length <= 0)
|
||||
return Enumerable.Empty<EntityBasic>();
|
||||
|
||||
var startNodes = new List<EntityBasic>();
|
||||
if (startNodeIds.Contains(-1))
|
||||
startNodes.Add(RootNode(textService.Localize(localizedKey)));
|
||||
|
||||
var mediaItems = entityService.GetAll(objectType, startNodeIds);
|
||||
startNodes.AddRange(Mapper.Map<IEnumerable<IUmbracoEntity>, IEnumerable<EntityBasic>>(mediaItems));
|
||||
return startNodes;
|
||||
}
|
||||
|
||||
private void MapUserGroupBasic(ISectionService sectionService, IEntityService entityService, ILocalizedTextService textService, dynamic group, UserGroupBasic display)
|
||||
{
|
||||
var allSections = sectionService.GetSections();
|
||||
display.Sections = allSections.Where(x => Enumerable.Contains(group.AllowedSections, x.Alias)).Select(Mapper.Map<ContentEditing.Section>);
|
||||
|
||||
if (group.StartMediaId > 0)
|
||||
{
|
||||
display.MediaStartNode = Mapper.Map<EntityBasic>(
|
||||
entityService.Get(group.StartMediaId, UmbracoObjectTypes.Media));
|
||||
}
|
||||
else if (group.StartMediaId == -1)
|
||||
{
|
||||
//create the root node
|
||||
display.MediaStartNode = RootNode(textService.Localize("media/mediaRoot"));
|
||||
}
|
||||
|
||||
if (group.StartContentId > 0)
|
||||
{
|
||||
display.ContentStartNode = Mapper.Map<EntityBasic>(
|
||||
entityService.Get(group.StartContentId, UmbracoObjectTypes.Document));
|
||||
}
|
||||
else if (group.StartContentId == -1)
|
||||
{
|
||||
//create the root node
|
||||
display.ContentStartNode = RootNode(textService.Localize("content/contentRoot"));
|
||||
}
|
||||
|
||||
if (display.Icon.IsNullOrWhiteSpace())
|
||||
{
|
||||
display.Icon = "icon-users";
|
||||
}
|
||||
}
|
||||
|
||||
private EntityBasic RootNode(string name)
|
||||
{
|
||||
return new EntityBasic
|
||||
{
|
||||
Name = name,
|
||||
Path = "-1",
|
||||
Icon = "icon-folder",
|
||||
Id = -1,
|
||||
Trashed = false,
|
||||
ParentId = -1
|
||||
};
|
||||
}
|
||||
|
||||
private static int GetIntId(object id)
|
||||
{
|
||||
var result = id.TryConvertTo<int>();
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
474
src/Umbraco.Web/Models/Mapping/UserModelMapper.cs
Normal file
474
src/Umbraco.Web/Models/Mapping/UserModelMapper.cs
Normal file
@@ -0,0 +1,474 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using AutoMapper;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Models.Mapping;
|
||||
using Umbraco.Core.Models.Membership;
|
||||
using Umbraco.Web.Models.ContentEditing;
|
||||
using umbraco;
|
||||
using umbraco.BusinessLogic.Actions;
|
||||
using Umbraco.Core.IO;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
using Umbraco.Core.Models.Identity;
|
||||
using Umbraco.Core.Security;
|
||||
using Umbraco.Core.Services;
|
||||
using UserProfile = Umbraco.Web.Models.ContentEditing.UserProfile;
|
||||
|
||||
namespace Umbraco.Web.Models.Mapping
|
||||
{
|
||||
internal class UserModelMapper : MapperConfiguration
|
||||
{
|
||||
public override void ConfigureMappings(IConfiguration config, ApplicationContext applicationContext)
|
||||
{
|
||||
|
||||
config.CreateMap<UserGroupSave, IUserGroup>()
|
||||
.ConstructUsing((UserGroupSave save) => new UserGroup() { CreateDate = DateTime.UtcNow })
|
||||
.IgnoreDeletableEntityCommonProperties()
|
||||
.ForMember(dest => dest.Id, map => map.Condition(source => GetIntId(source.Id) > 0))
|
||||
.ForMember(dest => dest.Id, map => map.MapFrom(source => GetIntId(source.Id)))
|
||||
//TODO: This is insane - but with our current version of AutoMapper when mapping from an existing object to another existing object, it will map the private fields which means the public setter is not used! wtf. So zpqrtbnk will laugh and say how crappy AutoMapper is... well he'll win this battle this time, so we need to do this in AfterMap to make sure the public setter is used so the property is dirty
|
||||
//.ForMember(dest => dest.Permissions, map => map.MapFrom(source => source.DefaultPermissions))
|
||||
.ForMember(dest => dest.Permissions, map => map.Ignore())
|
||||
.AfterMap((save, userGroup) =>
|
||||
{
|
||||
//TODO: See above comment
|
||||
userGroup.Permissions = save.DefaultPermissions;
|
||||
|
||||
userGroup.ClearAllowedSections();
|
||||
foreach (var section in save.Sections)
|
||||
{
|
||||
userGroup.AddAllowedSection(section);
|
||||
}
|
||||
});
|
||||
|
||||
//Used for merging existing UserSave to an existing IUser instance - this will not create an IUser instance!
|
||||
config.CreateMap<UserSave, IUser>()
|
||||
.IgnoreDeletableEntityCommonProperties()
|
||||
.ForMember(dest => dest.Id, map => map.Condition(source => GetIntId(source.Id) > 0))
|
||||
.ForMember(detail => detail.TourData, opt => opt.Ignore())
|
||||
.ForMember(detail => detail.SessionTimeout, opt => opt.Ignore())
|
||||
.ForMember(detail => detail.EmailConfirmedDate, opt => opt.Ignore())
|
||||
.ForMember(detail => detail.UserType, opt => opt.Ignore())
|
||||
.ForMember(detail => detail.StartContentId, opt => opt.Ignore())
|
||||
.ForMember(detail => detail.StartMediaId, opt => opt.Ignore())
|
||||
.ForMember(detail => detail.InvitedDate, opt => opt.Ignore())
|
||||
.ForMember(detail => detail.SecurityStamp, opt => opt.Ignore())
|
||||
.ForMember(detail => detail.Avatar, opt => opt.Ignore())
|
||||
.ForMember(detail => detail.ProviderUserKey, opt => opt.Ignore())
|
||||
.ForMember(detail => detail.RawPasswordValue, opt => opt.Ignore())
|
||||
.ForMember(detail => detail.RawPasswordAnswerValue, opt => opt.Ignore())
|
||||
.ForMember(detail => detail.PasswordQuestion, opt => opt.Ignore())
|
||||
.ForMember(detail => detail.Comments, opt => opt.Ignore())
|
||||
.ForMember(detail => detail.IsApproved, opt => opt.Ignore())
|
||||
.ForMember(detail => detail.IsLockedOut, opt => opt.Ignore())
|
||||
.ForMember(detail => detail.LastLoginDate, opt => opt.Ignore())
|
||||
.ForMember(detail => detail.LastPasswordChangeDate, opt => opt.Ignore())
|
||||
.ForMember(detail => detail.LastLockoutDate, opt => opt.Ignore())
|
||||
.ForMember(detail => detail.FailedPasswordAttempts, opt => opt.Ignore())
|
||||
.ForMember(user => user.Language, expression => expression.MapFrom(save => save.Culture))
|
||||
.AfterMap((save, user) =>
|
||||
{
|
||||
user.ClearGroups();
|
||||
var foundGroups = applicationContext.Services.UserService.GetUserGroupsByAlias(save.UserGroups.ToArray());
|
||||
foreach (var group in foundGroups)
|
||||
{
|
||||
user.AddGroup(group.ToReadOnlyGroup());
|
||||
}
|
||||
});
|
||||
|
||||
config.CreateMap<UserInvite, IUser>()
|
||||
.IgnoreDeletableEntityCommonProperties()
|
||||
.ForMember(detail => detail.Id, opt => opt.Ignore())
|
||||
.ForMember(detail => detail.TourData, opt => opt.Ignore())
|
||||
.ForMember(detail => detail.StartContentIds, opt => opt.Ignore())
|
||||
.ForMember(detail => detail.StartMediaIds, opt => opt.Ignore())
|
||||
.ForMember(detail => detail.UserType, opt => opt.Ignore())
|
||||
.ForMember(detail => detail.StartContentId, opt => opt.Ignore())
|
||||
.ForMember(detail => detail.StartMediaId, opt => opt.Ignore())
|
||||
.ForMember(detail => detail.Language, opt => opt.Ignore())
|
||||
.ForMember(detail => detail.Username, opt => opt.Ignore())
|
||||
.ForMember(detail => detail.PasswordQuestion, opt => opt.Ignore())
|
||||
.ForMember(detail => detail.SessionTimeout, opt => opt.Ignore())
|
||||
.ForMember(detail => detail.EmailConfirmedDate, opt => opt.Ignore())
|
||||
.ForMember(detail => detail.InvitedDate, opt => opt.Ignore())
|
||||
.ForMember(detail => detail.SecurityStamp, opt => opt.Ignore())
|
||||
.ForMember(detail => detail.Avatar, opt => opt.Ignore())
|
||||
.ForMember(detail => detail.ProviderUserKey, opt => opt.Ignore())
|
||||
.ForMember(detail => detail.RawPasswordValue, opt => opt.Ignore())
|
||||
.ForMember(detail => detail.RawPasswordAnswerValue, opt => opt.Ignore())
|
||||
.ForMember(detail => detail.Comments, opt => opt.Ignore())
|
||||
.ForMember(detail => detail.IsApproved, opt => opt.Ignore())
|
||||
.ForMember(detail => detail.IsLockedOut, opt => opt.Ignore())
|
||||
.ForMember(detail => detail.LastLoginDate, opt => opt.Ignore())
|
||||
.ForMember(detail => detail.LastPasswordChangeDate, opt => opt.Ignore())
|
||||
.ForMember(detail => detail.LastLockoutDate, opt => opt.Ignore())
|
||||
.ForMember(detail => detail.FailedPasswordAttempts, opt => opt.Ignore())
|
||||
//all invited users will not be approved, completing the invite will approve the user
|
||||
.ForMember(user => user.IsApproved, expression => expression.UseValue(false))
|
||||
.AfterMap((invite, user) =>
|
||||
{
|
||||
user.ClearGroups();
|
||||
var foundGroups = applicationContext.Services.UserService.GetUserGroupsByAlias(invite.UserGroups.ToArray());
|
||||
foreach (var group in foundGroups)
|
||||
{
|
||||
user.AddGroup(group.ToReadOnlyGroup());
|
||||
}
|
||||
});
|
||||
|
||||
config.CreateMap<IReadOnlyUserGroup, UserGroupBasic>()
|
||||
.ForMember(detail => detail.ContentStartNode, opt => opt.Ignore())
|
||||
.ForMember(detail => detail.UserCount, opt => opt.Ignore())
|
||||
.ForMember(detail => detail.MediaStartNode, opt => opt.Ignore())
|
||||
.ForMember(detail => detail.Key, opt => opt.Ignore())
|
||||
.ForMember(detail => detail.Sections, opt => opt.Ignore())
|
||||
.ForMember(detail => detail.Notifications, opt => opt.Ignore())
|
||||
.ForMember(detail => detail.Udi, opt => opt.Ignore())
|
||||
.ForMember(detail => detail.Trashed, opt => opt.Ignore())
|
||||
.ForMember(detail => detail.ParentId, opt => opt.UseValue(-1))
|
||||
.ForMember(detail => detail.Path, opt => opt.MapFrom(userGroup => "-1," + userGroup.Id))
|
||||
.ForMember(detail => detail.AdditionalData, opt => opt.Ignore())
|
||||
.AfterMap((group, display) =>
|
||||
{
|
||||
MapUserGroupBasic(applicationContext.Services, group, display);
|
||||
});
|
||||
|
||||
config.CreateMap<IUserGroup, UserGroupBasic>()
|
||||
.ForMember(detail => detail.ContentStartNode, opt => opt.Ignore())
|
||||
.ForMember(detail => detail.MediaStartNode, opt => opt.Ignore())
|
||||
.ForMember(detail => detail.Sections, opt => opt.Ignore())
|
||||
.ForMember(detail => detail.Notifications, opt => opt.Ignore())
|
||||
.ForMember(detail => detail.Udi, opt => opt.Ignore())
|
||||
.ForMember(detail => detail.Trashed, opt => opt.Ignore())
|
||||
.ForMember(detail => detail.ParentId, opt => opt.UseValue(-1))
|
||||
.ForMember(detail => detail.Path, opt => opt.MapFrom(userGroup => "-1," + userGroup.Id))
|
||||
.ForMember(detail => detail.AdditionalData, opt => opt.Ignore())
|
||||
.AfterMap((group, display) =>
|
||||
{
|
||||
MapUserGroupBasic(applicationContext.Services, group, display);
|
||||
});
|
||||
|
||||
//create a map to assign a user group's default permissions to the AssignedUserGroupPermissions instance
|
||||
config.CreateMap<IUserGroup, AssignedUserGroupPermissions>()
|
||||
.ForMember(detail => detail.Udi, opt => opt.Ignore())
|
||||
.ForMember(detail => detail.Trashed, opt => opt.Ignore())
|
||||
.ForMember(detail => detail.AdditionalData, opt => opt.Ignore())
|
||||
.ForMember(detail => detail.Id, opt => opt.MapFrom(group => group.Id))
|
||||
.ForMember(detail => detail.ParentId, opt => opt.UseValue(-1))
|
||||
.ForMember(detail => detail.Path, opt => opt.MapFrom(userGroup => "-1," + userGroup.Id))
|
||||
.ForMember(detail => detail.DefaultPermissions, expression => expression.ResolveUsing(new UserGroupDefaultPermissionsResolver(applicationContext.Services.TextService)))
|
||||
//these will be manually mapped and by default they are null
|
||||
.ForMember(detail => detail.AssignedPermissions, opt => opt.Ignore())
|
||||
.AfterMap((group, display) =>
|
||||
{
|
||||
if (display.Icon.IsNullOrWhiteSpace())
|
||||
{
|
||||
display.Icon = "icon-users";
|
||||
}
|
||||
});
|
||||
|
||||
config.CreateMap<UmbracoEntity, AssignedContentPermissions>()
|
||||
.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())
|
||||
.ForMember(x => x.AssignedPermissions, expression => expression.Ignore())
|
||||
.AfterMap((entity, basic) =>
|
||||
{
|
||||
if (entity.NodeObjectTypeId == Constants.ObjectTypes.MemberGuid && basic.Icon.IsNullOrWhiteSpace())
|
||||
{
|
||||
basic.Icon = "icon-user";
|
||||
}
|
||||
});
|
||||
|
||||
config.CreateMap<IUserGroup, UserGroupDisplay>()
|
||||
.ForMember(detail => detail.ContentStartNode, opt => opt.Ignore())
|
||||
.ForMember(detail => detail.MediaStartNode, opt => opt.Ignore())
|
||||
.ForMember(detail => detail.Sections, opt => opt.Ignore())
|
||||
.ForMember(detail => detail.Notifications, opt => opt.Ignore())
|
||||
.ForMember(detail => detail.Udi, opt => opt.Ignore())
|
||||
.ForMember(detail => detail.Trashed, opt => opt.Ignore())
|
||||
.ForMember(detail => detail.ParentId, opt => opt.UseValue(-1))
|
||||
.ForMember(detail => detail.Path, opt => opt.MapFrom(userGroup => "-1," + userGroup.Id))
|
||||
.ForMember(detail => detail.AdditionalData, opt => opt.Ignore())
|
||||
.ForMember(detail => detail.Users, opt => opt.Ignore())
|
||||
.ForMember(detail => detail.DefaultPermissions, expression => expression.ResolveUsing(new UserGroupDefaultPermissionsResolver(applicationContext.Services.TextService)))
|
||||
.ForMember(detail => detail.AssignedPermissions, opt => opt.Ignore())
|
||||
.AfterMap((group, display) =>
|
||||
{
|
||||
MapUserGroupBasic(applicationContext.Services, group, display);
|
||||
|
||||
//Important! Currently we are never mapping to multiple UserGroupDisplay objects but if we start doing that
|
||||
// this will cause an N+1 and we'll need to change how this works.
|
||||
var users = applicationContext.Services.UserService.GetAllInGroup(group.Id);
|
||||
display.Users = Mapper.Map<IEnumerable<UserBasic>>(users);
|
||||
|
||||
//Deal with assigned permissions:
|
||||
|
||||
var allContentPermissions = applicationContext.Services.UserService.GetPermissions(@group, true)
|
||||
.ToDictionary(x => x.EntityId, x => x);
|
||||
|
||||
IEnumerable<IUmbracoEntity> contentEntities;
|
||||
if (allContentPermissions.Keys.Count == 0)
|
||||
{
|
||||
contentEntities = new IUmbracoEntity[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
// a group can end up with way more than 2000 assigned permissions,
|
||||
// so we need to break them into groups in order to avoid breaking
|
||||
// the entity service due to too many Sql parameters.
|
||||
|
||||
var list = new List<IUmbracoEntity>();
|
||||
contentEntities = list;
|
||||
var entityService = applicationContext.Services.EntityService;
|
||||
foreach (var idGroup in allContentPermissions.Keys.InGroupsOf(2000))
|
||||
list.AddRange(entityService.GetAll(UmbracoObjectTypes.Document, idGroup.ToArray()));
|
||||
}
|
||||
|
||||
var allAssignedPermissions = new List<AssignedContentPermissions>();
|
||||
foreach (var entity in contentEntities)
|
||||
{
|
||||
var contentPermissions = allContentPermissions[entity.Id];
|
||||
|
||||
var assignedContentPermissions = Mapper.Map<AssignedContentPermissions>(entity);
|
||||
assignedContentPermissions.AssignedPermissions = AssignedUserGroupPermissions.ClonePermissions(display.DefaultPermissions);
|
||||
|
||||
//since there is custom permissions assigned to this node for this group, we need to clear all of the default permissions
|
||||
//and we'll re-check it if it's one of the explicitly assigned ones
|
||||
foreach (var permission in assignedContentPermissions.AssignedPermissions.SelectMany(x => x.Value))
|
||||
{
|
||||
permission.Checked = false;
|
||||
permission.Checked = contentPermissions.AssignedPermissions.Contains(permission.PermissionCode, StringComparer.InvariantCulture);
|
||||
}
|
||||
|
||||
allAssignedPermissions.Add(assignedContentPermissions);
|
||||
}
|
||||
|
||||
display.AssignedPermissions = allAssignedPermissions;
|
||||
});
|
||||
|
||||
//Important! Currently we are never mapping to multiple UserDisplay objects but if we start doing that
|
||||
// this will cause an N+1 and we'll need to change how this works.
|
||||
|
||||
config.CreateMap<IUser, UserDisplay>()
|
||||
.ForMember(detail => detail.Avatars, opt => opt.MapFrom(user => user.GetUserAvatarUrls(applicationContext.ApplicationCache.RuntimeCache)))
|
||||
.ForMember(detail => detail.Username, opt => opt.MapFrom(user => user.Username))
|
||||
.ForMember(detail => detail.LastLoginDate, opt => opt.MapFrom(user => user.LastLoginDate == default(DateTime) ? null : (DateTime?) user.LastLoginDate))
|
||||
.ForMember(detail => detail.UserGroups, opt => opt.MapFrom(user => user.Groups))
|
||||
.ForMember(detail => detail.Navigation, opt => opt.MapFrom(user => CreateUserEditorNavigation(applicationContext.Services.TextService)))
|
||||
.ForMember(
|
||||
detail => detail.CalculatedStartContentIds,
|
||||
opt => opt.MapFrom(user => GetStartNodeValues(
|
||||
user.CalculateContentStartNodeIds(applicationContext.Services.EntityService),
|
||||
applicationContext.Services.TextService,
|
||||
applicationContext.Services.EntityService,
|
||||
UmbracoObjectTypes.Document,
|
||||
"content/contentRoot")))
|
||||
.ForMember(
|
||||
detail => detail.CalculatedStartMediaIds,
|
||||
opt => opt.MapFrom(user => GetStartNodeValues(
|
||||
user.CalculateMediaStartNodeIds(applicationContext.Services.EntityService),
|
||||
applicationContext.Services.TextService,
|
||||
applicationContext.Services.EntityService,
|
||||
UmbracoObjectTypes.Media,
|
||||
"media/mediaRoot")))
|
||||
.ForMember(
|
||||
detail => detail.StartContentIds,
|
||||
opt => opt.MapFrom(user => GetStartNodeValues(
|
||||
user.StartContentIds.ToArray(),
|
||||
applicationContext.Services.TextService,
|
||||
applicationContext.Services.EntityService,
|
||||
UmbracoObjectTypes.Document,
|
||||
"content/contentRoot")))
|
||||
.ForMember(
|
||||
detail => detail.StartMediaIds,
|
||||
opt => opt.MapFrom(user => GetStartNodeValues(
|
||||
user.StartMediaIds.ToArray(),
|
||||
applicationContext.Services.TextService,
|
||||
applicationContext.Services.EntityService,
|
||||
UmbracoObjectTypes.Media,
|
||||
"media/mediaRoot")))
|
||||
.ForMember(detail => detail.Culture, opt => opt.MapFrom(user => user.GetUserCulture(applicationContext.Services.TextService)))
|
||||
.ForMember(
|
||||
detail => detail.AvailableCultures,
|
||||
opt => opt.MapFrom(user => applicationContext.Services.TextService.GetSupportedCultures().ToDictionary(x => x.Name, x => x.DisplayName)))
|
||||
.ForMember(
|
||||
detail => detail.EmailHash,
|
||||
opt => opt.MapFrom(user => user.Email.ToLowerInvariant().Trim().GenerateHash()))
|
||||
.ForMember(detail => detail.ParentId, opt => opt.UseValue(-1))
|
||||
.ForMember(detail => detail.Path, opt => opt.MapFrom(user => "-1," + user.Id))
|
||||
.ForMember(detail => detail.Notifications, opt => opt.Ignore())
|
||||
.ForMember(detail => detail.Udi, opt => opt.Ignore())
|
||||
.ForMember(detail => detail.Icon, opt => opt.Ignore())
|
||||
.ForMember(detail => detail.IsCurrentUser, opt => opt.Ignore())
|
||||
.ForMember(detail => detail.Trashed, opt => opt.Ignore())
|
||||
.ForMember(detail => detail.ResetPasswordValue, opt => opt.Ignore())
|
||||
.ForMember(detail => detail.Alias, opt => opt.Ignore())
|
||||
.ForMember(detail => detail.Trashed, opt => opt.Ignore())
|
||||
.ForMember(detail => detail.AdditionalData, opt => opt.Ignore());
|
||||
|
||||
config.CreateMap<IUser, UserBasic>()
|
||||
//Loading in the user avatar's requires an external request if they don't have a local file avatar, this means that initial load of paging may incur a cost
|
||||
//Alternatively, if this is annoying the back office UI would need to be updated to request the avatars for the list of users separately so it doesn't look
|
||||
//like the load time is waiting.
|
||||
.ForMember(detail =>
|
||||
detail.Avatars,
|
||||
opt => opt.MapFrom(user => user.GetUserAvatarUrls(applicationContext.ApplicationCache.RuntimeCache)))
|
||||
.ForMember(detail => detail.Username, opt => opt.MapFrom(user => user.Username))
|
||||
.ForMember(detail => detail.UserGroups, opt => opt.MapFrom(user => user.Groups))
|
||||
.ForMember(detail => detail.LastLoginDate, opt => opt.MapFrom(user => user.LastLoginDate == default(DateTime) ? null : (DateTime?) user.LastLoginDate))
|
||||
.ForMember(detail => detail.Culture, opt => opt.MapFrom(user => user.GetUserCulture(applicationContext.Services.TextService)))
|
||||
.ForMember(
|
||||
detail => detail.EmailHash,
|
||||
opt => opt.MapFrom(user => user.Email.ToLowerInvariant().Trim().ToMd5()))
|
||||
.ForMember(detail => detail.ParentId, opt => opt.UseValue(-1))
|
||||
.ForMember(detail => detail.Path, opt => opt.MapFrom(user => "-1," + user.Id))
|
||||
.ForMember(detail => detail.Notifications, opt => opt.Ignore())
|
||||
.ForMember(detail => detail.IsCurrentUser, opt => opt.Ignore())
|
||||
.ForMember(detail => detail.Udi, opt => opt.Ignore())
|
||||
.ForMember(detail => detail.Icon, opt => opt.Ignore())
|
||||
.ForMember(detail => detail.Trashed, opt => opt.Ignore())
|
||||
.ForMember(detail => detail.Alias, opt => opt.Ignore())
|
||||
.ForMember(detail => detail.Trashed, opt => opt.Ignore())
|
||||
.ForMember(detail => detail.AdditionalData, opt => opt.Ignore());
|
||||
|
||||
config.CreateMap<IUser, UserDetail>()
|
||||
.ForMember(detail => detail.Avatars, opt => opt.MapFrom(user => user.GetUserAvatarUrls(applicationContext.ApplicationCache.RuntimeCache)))
|
||||
.ForMember(detail => detail.UserId, opt => opt.MapFrom(user => GetIntId(user.Id)))
|
||||
.ForMember(detail => detail.StartContentIds, opt => opt.MapFrom(user => user.CalculateContentStartNodeIds(applicationContext.Services.EntityService)))
|
||||
.ForMember(detail => detail.StartMediaIds, opt => opt.MapFrom(user => user.CalculateMediaStartNodeIds(applicationContext.Services.EntityService)))
|
||||
.ForMember(detail => detail.Culture, opt => opt.MapFrom(user => user.GetUserCulture(applicationContext.Services.TextService)))
|
||||
.ForMember(
|
||||
detail => detail.EmailHash,
|
||||
opt => opt.MapFrom(user => user.Email.ToLowerInvariant().Trim().GenerateHash()))
|
||||
.ForMember(detail => detail.SecondsUntilTimeout, opt => opt.Ignore())
|
||||
.ForMember(detail => detail.UserGroups, opt => opt.Ignore())
|
||||
.AfterMap((user, detail) =>
|
||||
{
|
||||
//we need to map the legacy UserType
|
||||
//the best we can do here is to return the user's first user group as a IUserType object
|
||||
//but we should attempt to return any group that is the built in ones first
|
||||
var groups = user.Groups.ToArray();
|
||||
detail.UserGroups = user.Groups.Select(x => x.Alias).ToArray();
|
||||
|
||||
if (groups.Length == 0)
|
||||
{
|
||||
//In backwards compatibility land, a user type cannot be null! so we need to return a fake one.
|
||||
detail.UserType = "temp";
|
||||
}
|
||||
else
|
||||
{
|
||||
var builtIns = new[] { Constants.Security.AdminGroupAlias, "writer", "editor", Constants.Security.TranslatorGroupAlias };
|
||||
var foundBuiltIn = groups.FirstOrDefault(x => builtIns.Contains(x.Alias));
|
||||
if (foundBuiltIn != null)
|
||||
{
|
||||
detail.UserType = foundBuiltIn.Alias;
|
||||
}
|
||||
else
|
||||
{
|
||||
//otherwise return the first
|
||||
detail.UserType = groups[0].Alias;
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
config.CreateMap<IProfile, UserProfile>()
|
||||
.ForMember(detail => detail.UserId, opt => opt.MapFrom(profile => GetIntId(profile.Id)));
|
||||
}
|
||||
|
||||
private IEnumerable<EditorNavigation> CreateUserEditorNavigation(ILocalizedTextService textService)
|
||||
{
|
||||
return new[]
|
||||
{
|
||||
new EditorNavigation
|
||||
{
|
||||
Active = true,
|
||||
Alias = "details",
|
||||
Icon = "icon-umb-users",
|
||||
Name = textService.Localize("general/user"),
|
||||
View = "views/users/views/user/details.html"
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private IEnumerable<EntityBasic> GetStartNodeValues(int[] startNodeIds,
|
||||
ILocalizedTextService textService, IEntityService entityService, UmbracoObjectTypes objectType,
|
||||
string localizedKey)
|
||||
{
|
||||
if (startNodeIds.Length > 0)
|
||||
{
|
||||
var startNodes = new List<EntityBasic>();
|
||||
if (startNodeIds.Contains(-1))
|
||||
{
|
||||
startNodes.Add(RootNode(textService.Localize(localizedKey)));
|
||||
}
|
||||
var mediaItems = entityService.GetAll(objectType, startNodeIds);
|
||||
startNodes.AddRange(Mapper.Map<IEnumerable<IUmbracoEntity>, IEnumerable<EntityBasic>>(mediaItems));
|
||||
return startNodes;
|
||||
}
|
||||
return Enumerable.Empty<EntityBasic>();
|
||||
}
|
||||
|
||||
private void MapUserGroupBasic(ServiceContext services, dynamic group, UserGroupBasic display)
|
||||
{
|
||||
var allSections = services.SectionService.GetSections();
|
||||
display.Sections = allSections.Where(x => Enumerable.Contains(group.AllowedSections, x.Alias)).Select(Mapper.Map<ContentEditing.Section>);
|
||||
|
||||
if (group.StartMediaId > 0)
|
||||
{
|
||||
display.MediaStartNode = Mapper.Map<EntityBasic>(
|
||||
services.EntityService.Get(group.StartMediaId, UmbracoObjectTypes.Media));
|
||||
}
|
||||
else if (group.StartMediaId == -1)
|
||||
{
|
||||
//create the root node
|
||||
display.MediaStartNode = RootNode(services.TextService.Localize("media/mediaRoot"));
|
||||
}
|
||||
|
||||
if (group.StartContentId > 0)
|
||||
{
|
||||
display.ContentStartNode = Mapper.Map<EntityBasic>(
|
||||
services.EntityService.Get(group.StartContentId, UmbracoObjectTypes.Document));
|
||||
}
|
||||
else if (group.StartContentId == -1)
|
||||
{
|
||||
//create the root node
|
||||
display.ContentStartNode = RootNode(services.TextService.Localize("content/contentRoot"));
|
||||
}
|
||||
|
||||
if (display.Icon.IsNullOrWhiteSpace())
|
||||
{
|
||||
display.Icon = "icon-users";
|
||||
}
|
||||
}
|
||||
|
||||
private EntityBasic RootNode(string name)
|
||||
{
|
||||
return new EntityBasic
|
||||
{
|
||||
Name = name,
|
||||
Path = "-1",
|
||||
Icon = "icon-folder",
|
||||
Id = -1,
|
||||
Trashed = false,
|
||||
ParentId = -1
|
||||
};
|
||||
}
|
||||
|
||||
private static int GetIntId(object id)
|
||||
{
|
||||
var result = id.TryConvertTo<int>();
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user