/// <summary> /// Initializes a new instance of the <see cref="ContentEditorModelToTypedEntity<TContentModel, TTypedEntity>"/> class. /// </summary> /// <param name="engine">The engine.</param> /// <param name="resolverContext">The resolver context.</param> /// <param name="ignoreAttributeAliases">The attribute aliases to ignore during attribute mapping</param> public ContentEditorModelToTypedEntity(AbstractFluentMappingEngine engine, MapResolverContext resolverContext, params string[] ignoreAttributeAliases) : base(engine) { MappingContext .ForMember(x => x.UtcCreated, opt => opt.MapUsing <UtcCreatedMapper>()) .ForMember(x => x.UtcModified, opt => opt.MapUsing <UtcModifiedMapper>()) .IgnoreMember(x => x.RelationProxies) .MapMemberUsing(x => x.EntitySchema, new ContentEditorModelToEntitySchema <TContentModel>(MappingContext.Engine, resolverContext)) .AfterMap((source, dest) => { if (!source.ParentId.IsNullValueOrEmpty()) { // Enlist the specified parent dest.RelationProxies.EnlistParentById(source.ParentId, FixedRelationTypes.DefaultRelationType, source.OrdinalRelativeToParent); } //add or update all properties in the model. //We need to manually create a mapper for this operation in order to keep the object graph consistent //with the correct instances of objects. This mapper will ignore mapping the AttributeDef on the TypedAttribute //so that we can manually assign it from our EntitySchema. var compositeSchema = dest.EntitySchema as CompositeEntitySchema; var propertyMapper = new ContentPropertyToTypedAttribute(MappingContext.Engine, true); foreach (var contentProperty in source.Properties.Where(x => !ignoreAttributeAliases.Contains(x.Alias))) { var typedAttribute = propertyMapper.Map(contentProperty); //now we need to manually assign the same attribute definition instance from our already mapped schema. Func <AttributeDefinition, bool> findAttrib = x => x.Id.Value == contentProperty.DocTypePropertyId.Value; var attDef = dest.EntitySchema.AttributeDefinitions.SingleOrDefault(findAttrib); if (compositeSchema != null && attDef == null) { attDef = compositeSchema.AllAttributeDefinitions.Single(findAttrib); } typedAttribute.AttributeDefinition = attDef; dest.Attributes.SetValueOrAdd(typedAttribute); } //now we need to remove any properties that don't exist in the model, excluding the 'special' internal fields var allAliases = source.Properties.Select(x => x.Alias).ToArray(); var toRemove = dest.Attributes.Where(x => !allAliases.Contains(x.AttributeDefinition.Alias)) .Select(x => x.Id).ToArray(); dest.Attributes.RemoveAll(x => toRemove.Contains(x.Id)); }); }
/// <summary> /// Creates mappings for Cms View Models to/from Persistence/Hive models /// </summary> public void ConfigureModelToPersistenceMappings() { #region FileEditorModel -> File this.CreateMap <FileEditorModel, File>() .CreateUsing(x => new File()) .ForMember(x => x.ContentBytes, opt => opt.MapFrom(x => Encoding.UTF8.GetBytes(x.FileContent))); #endregion #region UserGroupEditorModel -> UserGroup this.CreateMap((new ContentEditorModelToTypedEntity <UserGroupEditorModel, UserGroup>(this, _resolverContext))) //ignore all custom properties as these need to be mapped by the underlying attributes .ForMember(x => x.Name, opt => opt.Ignore()); #endregion #region MemberEditorModel -> Member this.CreateMap <MemberEditorModel, Member>() .CreateUsing(x => new Member()) .ForMember(x => x.UtcCreated, opt => opt.MapUsing <UtcCreatedMapper>()) .ForMember(x => x.UtcModified, opt => opt.MapUsing <UtcModifiedMapper>()) .IgnoreMember(x => x.RelationProxies) .MapMemberFrom(x => x.EntitySchema, x => new MemberSchema()) .IgnoreMember(x => x.LastPasswordChangeDate) .IgnoreMember(x => x.LastActivityDate) .IgnoreMember(x => x.LastLoginDate) .IgnoreMember(x => x.IsApproved) .IgnoreMember(x => x.Email) .IgnoreMember(x => x.Username) .IgnoreMember(x => x.Name) .AfterMap((source, dest) => { //add or update all properties in the model. //We need to manually create a mapper for this operation in order to keep the object graph consistent //with the correct instances of objects. This mapper will ignore mapping the AttributeDef on the TypedAttribute //so that we can manually assign it from our EntitySchema. var propertyMapper = new ContentPropertyToTypedAttribute(this, true); foreach (var contentProperty in source.Properties.Where(x => x.Alias != MemberSchema.PasswordAlias)) { var typedAttribute = propertyMapper.Map(contentProperty); //now we need to manually assign the same attribute definition instance from our already mapped schema. var attDef = dest.EntitySchema.AttributeDefinitions.Single(x => x.Id.Value == contentProperty.DocTypePropertyId.Value); typedAttribute.AttributeDefinition = attDef; dest.Attributes.SetValueOrAdd(typedAttribute); } //now we need to remove any properties that don't exist in the model, excluding the 'special' internal fields var allAliases = source.Properties.Select(x => x.Alias).ToArray(); var toRemove = dest.Attributes.Where(x => !allAliases.Contains(x.AttributeDefinition.Alias)) .Select(x => x.Id).ToArray(); dest.Attributes.RemoveAll(x => toRemove.Contains(x.Id)); }); #endregion #region UserEditorModel -> User this.CreateMap(new UserEditorModelToTypedEntity(this, _resolverContext, MemberSchema.PasswordAlias)); #endregion #region ContentEditorModel -> Revision<TypedEntity> this.CreateMap(new ContentEditorModelToRevision <ContentEditorModel>(this)); #endregion #region MediaEditorModel -> Revision<TypedEntity> this.CreateMap(new ContentEditorModelToRevision <MediaEditorModel>(this)); #endregion #region DictionaryItemEditorModel -> Revision<TypedEntity> this.CreateMap(new ContentEditorModelToRevision <DictionaryItemEditorModel>(this)); #endregion #region ContentEditorModel -> TypedEntity this.CreateMap(new ContentEditorModelToTypedEntity <ContentEditorModel, TypedEntity>(this, _resolverContext)); #endregion #region MediaEditorModel -> TypedEntity this.CreateMap(new ContentEditorModelToTypedEntity <MediaEditorModel, TypedEntity>(this, _resolverContext)); #endregion #region DictionaryItemEditorModel -> TypedEntity this.CreateMap(new ContentEditorModelToTypedEntity <DictionaryItemEditorModel, TypedEntity>(this, _resolverContext)); #endregion #region DocumentTypeEditorModel -> EntitySchema this.CreateMap(new SchemaEditorModelToEntitySchema <DocumentTypeEditorModel>(this, _resolverContext, (from, to) => { if (!from.DefaultTemplateId.IsNullValueOrEmpty()) { to.SetXmlConfigProperty("default-template", from.DefaultTemplateId.Value); } //save the allowed templates as a list of ids to.SetXmlConfigProperty("allowed-templates", from.AllowedTemplates .Where(x => x.Selected) .Select(x => x.Value).ToArray()); })); #endregion #region MediaTypeEditorModel -> EntitySchema this.CreateMap(new SchemaEditorModelToEntitySchema <MediaTypeEditorModel>(this, _resolverContext)); #endregion #region ContentProperty -> TypedAttribute this.CreateMap(new ContentPropertyToTypedAttribute(this)); #endregion #region DataTypeEditorModel -> AttributeType this.CreateMap(new DataTypeToAttributeType <DataTypeEditorModel>( this, ((source, dest) => { dest.RenderTypeProvider = source.PropertyEditorId.ToString(); if (source.PreValueEditorModel != null) { dest.RenderTypeProviderConfig = source.PreValueEditorModel.GetSerializedValue(); } }))); #endregion #region DataType -> AttributeType this.CreateMap(new DataTypeToAttributeType <DataType>( this, ((source, dest) => { if (source.PropertyEditor != null) { dest.RenderTypeProvider = ((PropertyEditor)source.PropertyEditor).Id.ToString(); } dest.RenderTypeProviderConfig = source.Prevalues; }))); #endregion #region DocumentTypeProperty -> AttributeDefinition this.CreateMap(new DocumentTypePropertyToAttributeDefinition(this, _resolverContext)); #endregion #region Tab -> AttributeDefinitionGroup this.CreateMap <Tab, AttributeGroup>() .CreateUsing(x => new AttributeGroup() { Ordinal = x.SortOrder, Alias = x.Name }); #endregion #region HostnameEntryModel -> Hostname)) this.CreateMap <HostnameEntryModel, Hostname>() .CreateUsing(x => new Hostname()) .MapMemberFrom(x => x.Name, x => x.Hostname); #endregion #region PermissionStatusModel -> Relation this.CreateMap <PermissionStatusModel, RelationMetaDatum>() .CreateUsing(x => new RelationMetaDatum(x.PermissionId.ToString(), x.Status.HasValue ? x.Status.Value.ToString() : PermissionStatus.Inherit.ToString())); #endregion #region HostnamesModel -> IEnumerable<Hostname> this.CreateMap <HostnamesModel, IEnumerable <Hostname> >() .CreateUsing(x => new List <Hostname>()) .AfterMap((source, dest) => { //we are casting here only cuz we know we created it with a list, //if we are trying to map to existing, this isn't gonna work. var list = (List <Hostname>)dest; foreach (var h in source.AssignedHostnames.Select(Map <Hostname>)) { // Enlist the specified parent // TODO: The ContentEditorModel needs to supply us with the ordinal of the existing relationship, if it exists // otherwise we're always going to reset the Ordinal to 0 h.RelationProxies.EnlistParentById(source.Id, FixedRelationTypes.HostnameRelationType, 0); list.Add(h); } }); #endregion #region LanguageEditorModel -> LanguageElement this.CreateMap <LanguageEditorModel, LanguageElement>() .ForMember(x => x.Name, opt => opt.MapFrom(x => CultureInfo.GetCultureInfo(x.IsoCode).EnglishName)) .ForMember(x => x.Fallbacks, opt => opt.Ignore()) .AfterMap((s, t) => { t.Fallbacks.Clear(); foreach (var fallback in s.Fallbacks) { t.Fallbacks.Add(new FallbackElement { IsoCode = fallback }); } }); #endregion #region StylesheetRuleEditorModel -> StylesheetRule this.CreateMap <StylesheetRuleEditorModel, StylesheetRule>() .ForMember(x => x.RuleId, x => x.MapFrom(y => y.Id)) .ForMember(x => x.StylesheetId, x => x.MapFrom(y => y.ParentId)); #endregion }