/// <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; }
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); }
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 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 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 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 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); } }