protected virtual IDictionary <string, T> GetAllFieldsAsDictionary <T>(MappingData mappingData) { IDictionary <string, T> result = new Dictionary <string, T>(); if (mappingData.Fields != null) { foreach (KeyValuePair <string, object> field in mappingData.Fields) { if ((field.Key == "settings")) { throw new NotImplementedException("'settings' field handling"); // TODO } if (typeof(T) == typeof(KeywordModel)) { if (field.Value is KeywordModelData) { result[field.Key] = GetFieldValues <T>(field.Value, mappingData, resolveComponentLinks: false).FirstOrDefault(); } } else { result[field.Key] = GetFieldValues <T>(field.Value, mappingData, resolveComponentLinks: false).FirstOrDefault(); } } } if (mappingData.MetadataFields != null && typeof(T) == typeof(string)) { foreach (KeyValuePair <string, object> field in mappingData.MetadataFields) { result[field.Key] = GetFieldValues <T>(field.Value, mappingData, resolveComponentLinks: false).FirstOrDefault(); } } return(result); }
/// <summary> /// Builds a strongly typed Entity Model based on a given DXA R2 Data Model. /// </summary> /// <param name="entityModel">The strongly typed Entity Model to build. Is <c>null</c> for the first Entity Model Builder in the pipeline.</param> /// <param name="entityModelData">The DXA R2 Data Model.</param> /// <param name="baseModelType">The base type for the Entity Model to build.</param> /// <param name="localization">The context <see cref="ILocalization"/>.</param> public virtual void BuildEntityModel(ref EntityModel entityModel, EntityModelData entityModelData, Type baseModelType, ILocalization localization) { using (new Tracer(entityModel, entityModelData, baseModelType, localization)) { MvcData mvcData = CreateMvcData(entityModelData.MvcData, "Entity"); SemanticSchema semanticSchema = SemanticMapping.GetSchema(entityModelData.SchemaId, localization); Type modelType = (baseModelType == null) ? ModelTypeRegistry.GetViewModelType(mvcData) : semanticSchema.GetModelTypeFromSemanticMapping(baseModelType); MappingData mappingData = new MappingData { SourceViewModel = entityModelData, ModelType = modelType, PropertyValidation = new Validation { MainSchema = semanticSchema, InheritedSchemas = GetInheritedSemanticSchemas(entityModelData, localization) }, Fields = entityModelData.Content, MetadataFields = entityModelData.Metadata, Localization = localization }; entityModel = (EntityModel)CreateViewModel(mappingData); entityModel.Id = entityModelData.Id; entityModel.MvcData = mvcData ?? entityModel.GetDefaultView(localization); } }
protected virtual ViewModel CreateViewModel(MappingData mappingData) { ViewModelData viewModelData = mappingData.SourceViewModel; EntityModelData entityModelData = viewModelData as EntityModelData; ViewModel result; if (string.IsNullOrEmpty(mappingData.ModelId)) { // Use parameterless constructor result = (ViewModel)mappingData.ModelType.CreateInstance(); } else { // Pass model Identifier in constructor. result = (ViewModel)mappingData.ModelType.CreateInstance(mappingData.ModelId); } result.ExtensionData = viewModelData.ExtensionData; result.HtmlClasses = viewModelData.HtmlClasses; result.XpmMetadata = mappingData.Localization.IsXpmEnabled ? viewModelData.XpmMetadata : null; MediaItem mediaItem = result as MediaItem; if (mediaItem != null) { BinaryContentData binaryContent = entityModelData?.BinaryContent; if (binaryContent == null) { throw new DxaException( $"Unable to create Media Item ('{mappingData.ModelType.Name}') because the Data Model '{entityModelData?.Id}' does not contain Binary Content Data." ); } mediaItem.Url = binaryContent.Url; mediaItem.FileName = binaryContent.FileName; mediaItem.MimeType = binaryContent.MimeType; mediaItem.FileSize = binaryContent.FileSize; } EclItem eclItem = result as EclItem; if (eclItem != null) { ExternalContentData externalContent = entityModelData.ExternalContent; if (externalContent == null) { throw new DxaException( $"Unable to create ECL Item ('{mappingData.ModelType.Name}') because the Data Model '{entityModelData.Id}' does not contain External Content Data." ); } eclItem.EclDisplayTypeId = externalContent.DisplayTypeId; eclItem.EclTemplateFragment = externalContent.TemplateFragment; eclItem.EclExternalMetadata = externalContent.Metadata; eclItem.EclUri = externalContent.Id; } MapSemanticProperties(result, mappingData); return(result); }
protected virtual object MapKeyword(KeywordModelData keywordModelData, Type targetType, ILocalization localization) { if (typeof(KeywordModel).IsAssignableFrom(targetType)) { KeywordModel result; if (keywordModelData.SchemaId == null) { result = new KeywordModel { ExtensionData = keywordModelData.ExtensionData }; } else { MappingData keywordMappingData = new MappingData { SourceViewModel = keywordModelData, ModelType = targetType, PropertyValidation = new Validation { MainSchema = SemanticMapping.GetSchema(keywordModelData.SchemaId, localization), InheritedSchemas = GetInheritedSemanticSchemas(keywordModelData as ViewModelData, localization) }, MetadataFields = keywordModelData.Metadata, Localization = localization }; result = (KeywordModel)CreateViewModel(keywordMappingData); } result.Id = keywordModelData.Id; result.Title = keywordModelData.Title; result.Description = keywordModelData.Description ?? string.Empty; result.Key = keywordModelData.Key ?? string.Empty; result.TaxonomyId = keywordModelData.TaxonomyId; return(result); } if (targetType == typeof(Tag)) { return(new Tag { DisplayText = GetKeywordDisplayText(keywordModelData), Key = keywordModelData.Key, TagCategory = localization.GetCmUri(keywordModelData.TaxonomyId, (int)ItemType.Category) }); } if (targetType == typeof(bool)) { string key = string.IsNullOrEmpty(keywordModelData.Key) ? keywordModelData.Title : keywordModelData.Key; return(Convert.ToBoolean(key)); } return(GetKeywordDisplayText(keywordModelData)); }
/// <summary> /// Builds a strongly typed Page Model from a given DXA R2 Data Model. /// </summary> /// <param name="pageModel">The strongly typed Page Model to build. Is <c>null</c> for the first Page Model Builder in the pipeline.</param> /// <param name="pageModelData">The DXA R2 Data Model.</param> /// <param name="includePageRegions">Indicates whether Include Page Regions should be included.</param> /// <param name="localization">The context <see cref="ILocalization"/>.</param> public virtual void BuildPageModel(ref PageModel pageModel, PageModelData pageModelData, bool includePageRegions, ILocalization localization) { using (new Tracer(pageModel, pageModelData, includePageRegions, localization)) { MvcData mvcData = CreateMvcData(pageModelData.MvcData, "Page"); Type modelType = ModelTypeRegistry.GetViewModelType(mvcData); if (modelType == typeof(PageModel)) { // Standard Page Model. pageModel = new PageModel(pageModelData.Id) { ExtensionData = pageModelData.ExtensionData, HtmlClasses = pageModelData.HtmlClasses, XpmMetadata = localization.IsXpmEnabled ? pageModelData.XpmMetadata : null, }; } else if (pageModelData.SchemaId == null) { // Custom Page Model, but no custom metadata. pageModel = (PageModel)modelType.CreateInstance(pageModelData.Id); pageModel.ExtensionData = pageModelData.ExtensionData; pageModel.HtmlClasses = pageModelData.HtmlClasses; pageModel.XpmMetadata = localization.IsXpmEnabled ? pageModelData.XpmMetadata : null; } else { // Custom Page Model with custom metadata; do full-blown model mapping. MappingData mappingData = new MappingData { SourceViewModel = pageModelData, ModelId = pageModelData.Id, ModelType = modelType, PropertyValidation = new Validation { MainSchema = SemanticMapping.GetSchema(pageModelData.SchemaId, localization), InheritedSchemas = GetInheritedSemanticSchemas(pageModelData, localization) }, MetadataFields = pageModelData.Metadata, Localization = localization }; pageModel = (PageModel)CreateViewModel(mappingData); } pageModel.MvcData = mvcData; pageModel.Meta = pageModelData.Meta ?? new Dictionary <string, string>(); pageModel.Title = PostProcessPageTitle(pageModelData, localization); // TODO TSI-2210: This should eventually be done in Model Service. pageModel.Url = pageModelData.UrlPath; if (pageModelData.Regions != null) { IEnumerable <RegionModelData> regions = includePageRegions ? pageModelData.Regions : pageModelData.Regions.Where(r => r.IncludePageId == null); pageModel.Regions.UnionWith(regions.Select(data => CreateRegionModel(data, localization))); pageModel.IsVolatile |= pageModel.Regions.Any(region => region.IsVolatile); } } }
/// <summary> /// Initializes a new <see cref="MappingData"/> instance which is a (shallow) copy of another. /// </summary> /// <param name="other">The other <see cref="MappingData"/> instance to copy.</param> public MappingData(MappingData other) { TargetType = other.TargetType; Content = other.Content; Meta = other.Meta; TargetEntitiesByPrefix = other.TargetEntitiesByPrefix; SemanticSchema = other.SemanticSchema; EmbeddedSemanticSchemaField = other.EmbeddedSemanticSchemaField; EntityNames = other.EntityNames; ParentDefaultPrefix = other.ParentDefaultPrefix; EmbedLevel = other.EmbedLevel; SourceEntity = other.SourceEntity; ModelId = other.ModelId; Localization = other.Localization; ContextXPath = other.ContextXPath; }
protected virtual object MapEmbeddedFields(ContentModelData embeddedFields, Type targetType, SemanticSchemaField semanticSchemaField, string contextXPath, MappingData mappingData) { MappingData embeddedMappingData = new MappingData { ModelType = targetType, PropertyValidation = mappingData.PropertyValidation, EmbeddedSemanticSchemaField = semanticSchemaField, EmbedLevel = mappingData.EmbedLevel + 1, SourceViewModel = mappingData.SourceViewModel, Fields = embeddedFields, MetadataFields = embeddedFields, ContextXPath = contextXPath, Localization = mappingData.Localization }; return(CreateViewModel(embeddedMappingData)); }
protected virtual IEnumerable <T> GetFieldValues <T>(object fieldValues, MappingData mappingData, bool resolveComponentLinks) { if (!resolveComponentLinks) { // Handle Component Links here, because standard model mapping will resolve them. ILocalization localization = mappingData.Localization; if (fieldValues is EntityModelData) { return((new[] { localization.GetCmUri(((EntityModelData)fieldValues).Id) }) as IEnumerable <T>); } if (fieldValues is EntityModelData[]) { return(((EntityModelData[])fieldValues).Select(emd => localization.GetCmUri(emd.Id)) as IEnumerable <T>); } } // Use standard model mapping to map the field to a list of T. return(MapField(fieldValues, typeof(List <T>), null, mappingData) as IEnumerable <T>); }
protected virtual IDictionary <string, string> GetAllFieldsAsDictionary(MappingData mappingData) { IDictionary <string, string> result = new Dictionary <string, string>(); if (mappingData.Fields != null) { foreach (KeyValuePair <string, object> field in mappingData.Fields) { if ((field.Key == "settings")) { throw new NotImplementedException("'settings' field handling"); // TODO } result[field.Key] = GetFieldValuesAsStrings(field.Value, mappingData, resolveComponentLinks: false).FirstOrDefault(); } } if (mappingData.MetadataFields != null) { foreach (KeyValuePair <string, object> field in mappingData.MetadataFields) { result[field.Key] = GetFieldValuesAsStrings(field.Value, mappingData, resolveComponentLinks: false).FirstOrDefault(); } } return(result); }
protected virtual Dictionary<string, List<SemanticProperty>> FilterPropertySemanticsByEntity(Dictionary<string, List<SemanticProperty>> propertySemantics, MappingData mapData) { Dictionary<string, List<SemanticProperty>> filtered = new Dictionary<string, List<SemanticProperty>>(); foreach (KeyValuePair<string, List<SemanticProperty>> property in propertySemantics) { filtered.Add(property.Key, new List<SemanticProperty>()); List<SemanticProperty> defaultProperties = new List<SemanticProperty>(); foreach (SemanticProperty semanticProperty in property.Value) { //Default prefix is always OK, but should be added last if (string.IsNullOrEmpty(semanticProperty.Prefix)) { defaultProperties.Add(semanticProperty); } else { //Filter out any properties belonging to other entities than the source entity KeyValuePair<string, string>? entityData = GetEntityData(semanticProperty.Prefix, mapData.TargetEntitiesByPrefix, mapData.ParentDefaultPrefix); if (entityData != null && mapData.EntityNames!=null && mapData.EntityNames.Contains(entityData.Value.Key)) { if (mapData.EntityNames[entityData.Value.Key].First() == entityData.Value.Value) { filtered[property.Key].Add(semanticProperty); } } } } filtered[property.Key].AddRange(defaultProperties); } return filtered; }
protected virtual RegionModel CreateRegionModel(RegionModelData regionModelData, ILocalization localization) { MvcData mvcData = CreateMvcData(regionModelData.MvcData, "Region"); Type regionModelType = ModelTypeRegistry.GetViewModelType(mvcData); RegionModel result = (RegionModel)regionModelType.CreateInstance(regionModelData.Name); result.ExtensionData = regionModelData.ExtensionData; result.HtmlClasses = regionModelData.HtmlClasses; result.MvcData = mvcData; result.XpmMetadata = localization.IsXpmEnabled ? regionModelData.XpmMetadata : null; result.SchemaId = regionModelData.SchemaId; if (!string.IsNullOrEmpty(regionModelData.SchemaId)) { SemanticSchema semanticSchema = SemanticMapping.GetSchema(regionModelData.SchemaId, localization); Type modelType = ModelTypeRegistry.GetViewModelType(mvcData); MappingData mappingData = new MappingData { SourceViewModel = regionModelData, ModelType = modelType, PropertyValidation = new Validation { MainSchema = semanticSchema, InheritedSchemas = GetInheritedSemanticSchemas(regionModelData, localization) }, Fields = null, MetadataFields = regionModelData.Metadata, Localization = localization }; MapSemanticProperties(result, mappingData); } if (regionModelData.Regions != null) { IEnumerable <RegionModel> nestedRegionModels = regionModelData.Regions.Select(data => CreateRegionModel(data, localization)); result.Regions.UnionWith(nestedRegionModels); result.IsVolatile |= result.Regions.Any(region => region.IsVolatile); } if (regionModelData.Entities != null) { foreach (EntityModelData entityModelData in regionModelData.Entities) { EntityModel entityModel; try { entityModel = ModelBuilderPipeline.CreateEntityModel(entityModelData, null, localization); // indicate to region model that this region is potentially volatile if it contains a volatile entity result.IsVolatile |= entityModel.IsVolatile; entityModel.MvcData.RegionName = regionModelData.Name; } catch (Exception ex) { // If there is a problem mapping an Entity, we replace it with an ExceptionEntity which holds the error details and carry on. Log.Error(ex); entityModel = new ExceptionEntity(ex); } result.Entities.Add(entityModel); } } return(result); }
protected virtual object MapField(object fieldValues, Type modelPropertyType, SemanticSchemaField semanticSchemaField, MappingData mappingData) { Type sourceType = fieldValues.GetType(); bool isArray = sourceType.IsArray; if (isArray) { sourceType = sourceType.GetElementType(); } bool isListProperty = modelPropertyType.IsGenericList(); Type targetType = modelPropertyType.GetUnderlyingGenericListType() ?? modelPropertyType; // Convert.ChangeType cannot convert non-nullable types to nullable types, so don't try that. Type bareTargetType = modelPropertyType.GetUnderlyingNullableType() ?? targetType; IList mappedValues = targetType.CreateGenericList(); if (typeof(EntityModel).IsAssignableFrom(targetType) && sourceType != typeof(EntityModelData) && (sourceType == typeof(string) && string.IsNullOrEmpty((string)fieldValues))) { return(isListProperty ? mappedValues : null); } switch (sourceType.Name) { case "String": if (isArray) { foreach (string fieldValue in (string[])fieldValues) { mappedValues.Add(MapString(fieldValue, bareTargetType)); } } else { mappedValues.Add(MapString((string)fieldValues, bareTargetType)); } break; case "DateTime": case "Single": case "Double": if (isArray) { foreach (object fieldValue in (Array)fieldValues) { mappedValues.Add(Convert.ChangeType(fieldValue, bareTargetType)); } } else { mappedValues.Add(Convert.ChangeType(fieldValues, bareTargetType)); } break; case "RichTextData": if (isArray) { foreach (RichTextData fieldValue in (RichTextData[])fieldValues) { mappedValues.Add(MapRichText(fieldValue, targetType, mappingData.Localization)); } } else { mappedValues.Add(MapRichText((RichTextData)fieldValues, targetType, mappingData.Localization)); } break; case "ContentModelData": string fieldXPath = semanticSchemaField.GetXPath(mappingData.ContextXPath); if (isArray) { int index = 1; foreach (ContentModelData embeddedFields in (ContentModelData[])fieldValues) { string indexedFieldXPath = $"{fieldXPath}[{index++}]"; mappedValues.Add(MapEmbeddedFields(embeddedFields, targetType, semanticSchemaField, indexedFieldXPath, mappingData)); } } else { string indexedFieldXPath = $"{fieldXPath}[1]"; mappedValues.Add(MapEmbeddedFields((ContentModelData)fieldValues, targetType, semanticSchemaField, indexedFieldXPath, mappingData)); } break; case "EntityModelData": if (isArray) { foreach (EntityModelData entityModelData in (EntityModelData[])fieldValues) { mappedValues.Add(MapComponentLink(entityModelData, targetType, mappingData.Localization)); } } else { mappedValues.Add(MapComponentLink((EntityModelData)fieldValues, targetType, mappingData.Localization)); } break; case "KeywordModelData": if (isArray) { foreach (KeywordModelData keywordModelData in (KeywordModelData[])fieldValues) { mappedValues.Add(MapKeyword(keywordModelData, targetType, mappingData.Localization)); } } else { mappedValues.Add(MapKeyword((KeywordModelData)fieldValues, targetType, mappingData.Localization)); } break; default: throw new DxaException($"Unexpected field type: '{sourceType.Name}'."); } return(isListProperty ? mappedValues : ((mappedValues.Count == 0) ? null : mappedValues[0])); }
private static IField GetFieldFromSemantics(MappingData mapData, SemanticProperty info) { KeyValuePair<string, string>? entityData = GetEntityData(info.Prefix, mapData.TargetEntitiesByPrefix, mapData.ParentDefaultPrefix); if (entityData != null) { // determine field semantics string vocab = entityData.Value.Key; string prefix = SemanticMapping.GetPrefix(vocab, mapData.Localization); if (prefix != null && mapData.EntityNames!=null) { string property = info.PropertyName; string entity = mapData.EntityNames[vocab].FirstOrDefault(); if (entity != null && mapData.SemanticSchema!=null) { FieldSemantics fieldSemantics = new FieldSemantics(prefix, entity, property); // locate semantic schema field SemanticSchemaField matchingField = mapData.SemanticSchema.FindFieldBySemantics(fieldSemantics); if (matchingField != null) { return ExtractMatchedField(matchingField, (matchingField.IsMetadata && mapData.Meta!=null) ? mapData.Meta : mapData.Content, mapData.EmbedLevel); } } } } return null; }
protected virtual void MapSemanticProperties(ViewModel viewModel, MappingData mappingData) { Type modelType = viewModel.GetType(); IDictionary <string, List <SemanticProperty> > propertySemanticsMap = ModelTypeRegistry.GetPropertySemantics(modelType); IDictionary <string, string> xpmPropertyMetadata = new Dictionary <string, string>(); Validation validation = mappingData.PropertyValidation; foreach (KeyValuePair <string, List <SemanticProperty> > propertySemantics in propertySemanticsMap) { PropertyInfo modelProperty = modelType.GetProperty(propertySemantics.Key); List <SemanticProperty> semanticProperties = propertySemantics.Value; bool isFieldMapped = false; string fieldXPath = null; foreach (SemanticProperty semanticProperty in semanticProperties) { if (semanticProperty.PropertyName == SemanticProperty.AllFields) { modelProperty.SetValue(viewModel, GetAllFieldsAsDictionary(mappingData)); isFieldMapped = true; break; } if ((semanticProperty.PropertyName == SemanticProperty.Self) && validation.MainSchema.HasSemanticType(semanticProperty.SemanticType)) { try { object mappedSelf = MapComponentLink((EntityModelData)mappingData.SourceViewModel, modelProperty.PropertyType, mappingData.Localization); modelProperty.SetValue(viewModel, mappedSelf); isFieldMapped = true; break; } catch (Exception ex) { Log.Debug($"Self mapping failed for {modelType.Name}.{modelProperty.Name}: {ex.Message}"); continue; } } FieldSemantics fieldSemantics = new FieldSemantics( semanticProperty.SemanticType.Vocab, semanticProperty.SemanticType.EntityName, semanticProperty.PropertyName, null); SemanticSchemaField semanticSchemaField = (mappingData.EmbeddedSemanticSchemaField == null) ? ValidateField(validation, fieldSemantics) : mappingData.EmbeddedSemanticSchemaField.FindFieldBySemantics(fieldSemantics); if (semanticSchemaField == null) { // No matching Semantic Schema Field found for this Semantic Property; maybe another one will match. continue; } // Matching Semantic Schema Field found fieldXPath = IsFieldFromMainSchema(validation, fieldSemantics) ? semanticSchemaField.GetXPath(mappingData.ContextXPath) : null; ContentModelData fields = semanticSchemaField.IsMetadata ? mappingData.MetadataFields : mappingData.Fields; object fieldValue = FindFieldValue(semanticSchemaField, fields, mappingData.EmbedLevel); if (fieldValue == null) { // No field value found; maybe we will find a value for another Semantic Property. continue; } try { object mappedPropertyValue = MapField(fieldValue, modelProperty.PropertyType, semanticSchemaField, mappingData); modelProperty.SetValue(viewModel, mappedPropertyValue); } catch (Exception ex) { throw new DxaException( $"Unable to map field '{semanticSchemaField.Name}' to property {modelType.Name}.{modelProperty.Name} of type '{modelProperty.PropertyType.FullName}'.", ex); } isFieldMapped = true; break; } if (fieldXPath != null) { xpmPropertyMetadata.Add(modelProperty.Name, fieldXPath); } else if (!isFieldMapped && Log.IsDebugEnabled) { string formattedSemanticProperties = string.Join(", ", semanticProperties.Select(sp => sp.ToString())); Log.Debug( $"Property {modelType.Name}.{modelProperty.Name} cannot be mapped to a CM field of {validation.MainSchema}. Semantic properties: {formattedSemanticProperties}."); } } EntityModel entityModel = viewModel as EntityModel; if ((entityModel != null) && mappingData.Localization.IsXpmEnabled) { entityModel.XpmPropertyMetadata = xpmPropertyMetadata; } }
public virtual void BuildEntityModel(ref EntityModel entityModel, IComponent component, Type baseModelType, Localization localization) { using (new Tracer(entityModel, component, baseModelType, localization)) { string[] schemaTcmUriParts = component.Schema.Id.Split('-'); SemanticSchema semanticSchema = SemanticMapping.GetSchema(schemaTcmUriParts[1], localization); // The semantic mapping may resolve to a more specific model type than specified by the View Model itself (e.g. Image instead of just MediaItem for Teaser.Media) Type modelType = semanticSchema.GetModelTypeFromSemanticMapping(baseModelType); MappingData mappingData = new MappingData { SemanticSchema = semanticSchema, EntityNames = semanticSchema.GetEntityNames(), TargetEntitiesByPrefix = GetEntityDataFromType(modelType), Content = component.Fields, Meta = component.MetadataFields, TargetType = modelType, SourceEntity = component, Localization = localization }; entityModel = (EntityModel)CreateViewModel(mappingData); entityModel.Id = GetDxaIdentifierFromTcmUri(component.Id); entityModel.XpmMetadata = GetXpmMetadata(component); if (entityModel is MediaItem && component.Multimedia != null && component.Multimedia.Url != null) { MediaItem mediaItem = (MediaItem)entityModel; mediaItem.Url = component.Multimedia.Url; mediaItem.FileName = component.Multimedia.FileName; mediaItem.FileSize = component.Multimedia.Size; mediaItem.MimeType = component.Multimedia.MimeType; } if (entityModel is Link) { Link link = (Link)entityModel; if (String.IsNullOrEmpty(link.Url)) { link.Url = SiteConfiguration.LinkResolver.ResolveLink(component.Id); } } } }
private PageModel CreatePageModel(IPage page, Localization localization) { MvcData pageMvcData = GetMvcData(page); Type pageModelType = ModelTypeRegistry.GetViewModelType(pageMvcData); string pageId = GetDxaIdentifierFromTcmUri(page.Id); ISchema pageMetadataSchema = page.Schema; PageModel pageModel; if (pageModelType == typeof(PageModel)) { // Standard Page Model pageModel = new PageModel(pageId); } else if (pageMetadataSchema == null) { // Custom Page Model but no Page metadata that can be mapped; simply create a Page Model instance of the right type. pageModel = (PageModel)Activator.CreateInstance(pageModelType, pageId); } else { // Custom Page Model and Page metadata is present; do full-blown model mapping. string[] schemaTcmUriParts = pageMetadataSchema.Id.Split('-'); SemanticSchema semanticSchema = SemanticMapping.GetSchema(schemaTcmUriParts[1], localization); MappingData mappingData = new MappingData { TargetType = pageModelType, SemanticSchema = semanticSchema, EntityNames = semanticSchema.GetEntityNames(), TargetEntitiesByPrefix = GetEntityDataFromType(pageModelType), Meta = page.MetadataFields, ModelId = pageId, Localization = localization }; pageModel = (PageModel) CreateViewModel(mappingData); } pageModel.MvcData = pageMvcData; pageModel.XpmMetadata = GetXpmMetadata(page); pageModel.Title = page.Title; // add html classes to model from metadata // TODO: move to CreateViewModel so it can be merged with the same code for a Component/ComponentTemplate IPageTemplate template = page.PageTemplate; if (template.MetadataFields != null && template.MetadataFields.ContainsKey("htmlClasses")) { // strip illegal characters to ensure valid html in the view (allow spaces for multiple classes) pageModel.HtmlClasses = template.MetadataFields["htmlClasses"].Value.StripIllegalCharacters(@"[^\w\-\ ]"); } return pageModel; }
private ViewModel MapEmbeddedFields(IFieldSet embeddedFields, Type modelType, MappingData mapData) { MappingData embeddedMappingData = new MappingData { TargetType = modelType, Content = embeddedFields, Meta = null, EntityNames = mapData.EntityNames, // TODO: should this not be re-determined for the embedded model type? ParentDefaultPrefix = mapData.ParentDefaultPrefix, TargetEntitiesByPrefix = mapData.TargetEntitiesByPrefix, // TODO: should this not be re-determined for the embedded model type? SemanticSchema = mapData.SemanticSchema, // TODO: should this not be re-determined for the embedded model type? EmbedLevel = mapData.EmbedLevel + 1, Localization = mapData.Localization }; return CreateViewModel(embeddedMappingData); }
protected virtual ViewModel CreateViewModel(MappingData mappingData) { Type modelType = mappingData.TargetType; // TODO: why is this not a separate parameter? ViewModel model; if (string.IsNullOrEmpty(mappingData.ModelId)) { // Use parameterless constructor model = (ViewModel)Activator.CreateInstance(modelType); } else { // Pass model Identifier in constructor. model = (ViewModel)Activator.CreateInstance(modelType, mappingData.ModelId); } Dictionary<string, string> xpmPropertyMetadata = new Dictionary<string, string>(); Dictionary<string, List<SemanticProperty>> propertySemantics = LoadPropertySemantics(modelType); propertySemantics = FilterPropertySemanticsByEntity(propertySemantics, mappingData); foreach (PropertyInfo pi in modelType.GetProperties()) { bool multival = pi.PropertyType.IsGenericType && (pi.PropertyType.GetGenericTypeDefinition() == typeof(List<>)); Type propertyType = multival ? pi.PropertyType.GetGenericArguments()[0] : pi.PropertyType; if (propertySemantics.ContainsKey(pi.Name)) { foreach (SemanticProperty info in propertySemantics[pi.Name]) { IField field = GetFieldFromSemantics(mappingData, info); if (field != null && (field.Values.Count > 0 || field.EmbeddedValues.Count > 0)) { pi.SetValue(model, MapFieldValues(field, propertyType, multival, mappingData)); xpmPropertyMetadata.Add(pi.Name, GetFieldXPath(field)); break; } // Special mapping cases require SourceEntity to be set if (mappingData.SourceEntity == null) { continue; } bool processed = false; if (info.PropertyName == "_self") { //Map the whole entity to an image property, or a resolved link to the entity to a Url field if (typeof(MediaItem).IsAssignableFrom(propertyType) || typeof(Link).IsAssignableFrom(propertyType) || propertyType == typeof(String)) { object mappedSelf = MapComponent(mappingData.SourceEntity, propertyType, mappingData.Localization); if (multival) { IList genericList = CreateGenericList(propertyType); genericList.Add(mappedSelf); pi.SetValue(model, genericList); } else { pi.SetValue(model, mappedSelf); } processed = true; } } else if (info.PropertyName == "_all" && pi.PropertyType == typeof(Dictionary<string, string>)) { //Map all fields into a single (Dictionary) property pi.SetValue(model, GetAllFieldsAsDictionary(mappingData.SourceEntity)); processed = true; } if (processed) { break; } } } } EntityModel entityModel = model as EntityModel; if (entityModel != null) { entityModel.XpmPropertyMetadata = xpmPropertyMetadata; } return model; }
private static IEnumerable <string> GetFieldValuesAsStrings(object fieldValues, MappingData mappingData, bool resolveComponentLinks) { if (!resolveComponentLinks) { // Handle Component Links here, because standard model mapping will resolve them. Localization localization = mappingData.Localization; if (fieldValues is EntityModelData) { return(new[] { localization.GetCmUri(((EntityModelData)fieldValues).Id) }); } if (fieldValues is EntityModelData[]) { return(((EntityModelData[])fieldValues).Select(emd => localization.GetCmUri(emd.Id))); } } // Use standard model mapping to map the field to a list of strings. return((IEnumerable <string>)MapField(fieldValues, typeof(List <string>), null, mappingData)); }
private object MapFieldValues(IField field, Type modelType, bool multival, MappingData mapData) { try { // Convert.ChangeType cannot convert non-nullable types to nullable types, so don't try that. Type bareModelType = modelType; if (modelType.IsGenericType && modelType.GetGenericTypeDefinition() == typeof(Nullable<>)) { bareModelType = modelType.GenericTypeArguments[0]; } IList mappedValues = CreateGenericList(modelType); switch (field.FieldType) { case FieldType.Date: foreach (DateTime value in field.DateTimeValues) { mappedValues.Add(Convert.ChangeType(value, bareModelType)); } break; case FieldType.Number: foreach (Double value in field.NumericValues) { mappedValues.Add(Convert.ChangeType(value, bareModelType)); } break; case FieldType.MultiMediaLink: case FieldType.ComponentLink: foreach (IComponent value in field.LinkedComponentValues) { mappedValues.Add(MapComponent(value, modelType, mapData.Localization)); } break; case FieldType.Embedded: foreach (IFieldSet value in field.EmbeddedValues) { mappedValues.Add(MapEmbeddedFields(value, modelType, mapData)); } break; case FieldType.Keyword: foreach (IKeyword value in field.Keywords) { mappedValues.Add(MapKeyword(value, modelType)); } break; case FieldType.Xhtml: IRichTextProcessor richTextProcessor = SiteConfiguration.RichTextProcessor; foreach (string value in field.Values) { RichText richText = richTextProcessor.ProcessRichText(value, mapData.Localization); if (modelType == typeof(string)) { mappedValues.Add(richText.ToString()); } else { mappedValues.Add(richText); } } break; default: foreach (string value in field.Values) { object mappedValue = (modelType == typeof(RichText)) ? new RichText(value) : Convert.ChangeType(value, bareModelType); mappedValues.Add(mappedValue); } break; } if (multival) { return mappedValues; } return mappedValues.Count == 0 ? null : mappedValues[0]; } catch (Exception ex) { throw new DxaException(String.Format("Unable to map field '{0}' to property of type '{1}'.", field.Name, modelType.FullName), ex); } }