/// <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);
            }
        }
Exemple #2
0
        /// <summary>
        /// Generates semantic markup (HTML/RDFa attributes) for a given property of a given Entity Model.
        /// </summary>
        /// <param name="entityModel">The Entity Model which contains the property.</param>
        /// <param name="propertyInfo">The reflected property info.</param>
        /// <param name="index">The index of the property value (for multi-value properties).</param>
        /// <returns>The semantic markup (HTML/RDFa attributes).</returns>
        internal static MvcHtmlString RenderPropertyAttributes(EntityModel entityModel, MemberInfo propertyInfo, int index = 0)
        {
            string markup       = string.Empty;
            string propertyName = propertyInfo.Name;

            string[] semanticPropertyNames = ModelTypeRegistry.GetSemanticPropertyNames(propertyInfo.DeclaringType, propertyName);
            if (semanticPropertyNames != null && semanticPropertyNames.Any())
            {
                markup = $"property=\"{string.Join(" ", semanticPropertyNames)}\"";
            }

            if (WebRequestContext.IsPreview)
            {
                string xpmMarkupAttr = RenderXpmMarkupAttribute(entityModel, propertyName, index);
                if (string.IsNullOrEmpty(markup))
                {
                    markup = xpmMarkupAttr;
                }
                else
                {
                    markup += " " + xpmMarkupAttr;
                }
            }

            return(new MvcHtmlString(markup));
        }
Exemple #3
0
        /// <summary>
        /// Registers a View Model and associated View.
        /// </summary>
        /// <param name="viewName">The name of the View to register.</param>
        /// <param name="modelType">The View Model Type to associate with the View. Must be a subclass of Type <see cref="ViewModel"/>.</param>
        /// <param name="controllerName">The Controller name. If not specified (or <c>null</c>), the Controller name is inferred from the <see cref="modelType"/>: either "Entity", "Region" or "Page".</param>
        private static void RegisterViewModel(string viewName, Type modelType, string controllerName = null)
        {
            if (String.IsNullOrEmpty(controllerName))
            {
                if (typeof(EntityModel).IsAssignableFrom(modelType))
                {
                    controllerName = "Entity";
                }
                else if (typeof(RegionModel).IsAssignableFrom(modelType))
                {
                    controllerName = "Region";
                }
                else
                {
                    controllerName = "Page";
                }
            }

            MvcData mvcData = new MvcData(viewName)
            {
                ControllerName = controllerName
            };

            ModelTypeRegistry.RegisterViewModel(mvcData, modelType);
        }
Exemple #4
0
 /// <summary>
 /// Registers this View Model Type.
 /// </summary>
 /// <remarks>
 /// Although this View Model Type is part of the DXA Framework, it has to be registered like any other View Model Type.
 /// In order to work with Tridion Docs content, it will be associated with specific MVC data.
 /// A DXA Web Application/Module that wants to work with Tridion Docs content should call this method
 /// unless it defines its own View Model Type for generic Topics.
 /// </remarks>
 public static void Register()
 {
     using (new Tracer())
     {
         ModelTypeRegistry.RegisterViewModel(new MvcData("Ish:Entity:Topic"), typeof(GenericTopic));
     }
 }
Exemple #5
0
        /// <summary>
        /// Generates semantic markup (HTML/RDFa attributes) for a given Entity Model.
        /// </summary>
        /// <param name="entityModel">The Entity Model.</param>
        /// <returns>The semantic markup (HTML/RDFa attributes).</returns>
        internal static MvcHtmlString RenderEntityAttributes(EntityModel entityModel)
        {
            string markup = string.Empty;

            IDictionary <string, string> prefixMappings;

            string[] semanticTypes = ModelTypeRegistry.GetSemanticTypes(entityModel.GetType(), out prefixMappings);
            if (semanticTypes.Any())
            {
                markup =
                    $"prefix=\"{string.Join(" ", prefixMappings.Select(pm => $"{pm.Key}: {pm.Value}"))}\" typeof=\"{string.Join(" ", semanticTypes)}\"";
            }

            if (WebRequestContext.IsPreview)
            {
                string xpmMarkupAttr = RenderXpmMarkupAttribute(entityModel);
                if (string.IsNullOrEmpty(markup))
                {
                    markup = xpmMarkupAttr;
                }
                else
                {
                    markup += " " + xpmMarkupAttr;
                }
            }

            return(new MvcHtmlString(markup));
        }
        /// <summary>
        /// Determine a Model Type based on semantic mappings (and a given base model type).
        /// </summary>
        /// <param name="baseModelType">The base type as obtained from the View Model.</param>
        /// <returns>The given base Model Type or a subclass if a more specific class can be resolved via semantic mapping.</returns>
        /// <remarks>
        /// This method makes it possible (for example) to let the <see cref="Teaser.Media"/> property get an instance of <see cref="Image"/>
        /// rather than just <see cref="MediaItem"/> (the type of the View Model property).
        /// </remarks>
        public Type GetModelTypeFromSemanticMapping(Type baseModelType)
        {
            Type[]   foundAmbiguousMappings = null;
            string[] semanticTypeNames      = GetSemanticTypeNames();
            foreach (string semanticTypeName in semanticTypeNames)
            {
                IEnumerable <Type> mappedModelTypes = ModelTypeRegistry.GetMappedModelTypes(semanticTypeName);
                if (mappedModelTypes == null)
                {
                    continue;
                }

                Type[] matchingModelTypes = mappedModelTypes.Where(t => baseModelType.IsAssignableFrom(t)).ToArray();
                if (matchingModelTypes.Length == 1)
                {
                    // Exactly one matching model type; return it.
                    return(matchingModelTypes[0]);
                }

                if (matchingModelTypes.Length > 1)
                {
                    // Multiple candidate models types found. Continue scanning; maybe we'll find a unique one for another semantic type.
                    foundAmbiguousMappings = matchingModelTypes;
                }
            }

            string errorMessage;

            if (foundAmbiguousMappings == null)
            {
                errorMessage =
                    $"No semantic mapping found between Schema {Id} ({String.Join(", ", semanticTypeNames)}) and model type '{baseModelType.FullName}'";
            }
            else
            {
                errorMessage =
                    $"Ambiguous semantic mappings found between Schema {Id} ({String.Join(", ", semanticTypeNames)}) and model type '{String.Join(", ", foundAmbiguousMappings.Select(t => t.FullName))}'. Found types: {baseModelType.FullName}";
            }

            if (baseModelType.IsAbstract)
            {
                // Base model type is abstract and we didn't find an (unambigous) concrete subtype to instantiate.
                throw new DxaException(errorMessage);
            }

            // Base model type is concrete, so we can fall back to instantiating that type.
            if (foundAmbiguousMappings == null)
            {
                Log.Debug("{0}. Sticking with model type.", errorMessage);
            }
            else
            {
                Log.Warn("{0}. Sticking with model type.", errorMessage);
            }

            return(baseModelType);
        }
        /// <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);
                }
            }
        }
Exemple #8
0
 protected virtual Dictionary <string, List <SemanticProperty> > LoadPropertySemantics(Type type)
 {
     lock (_semanticPropertiesCache)
     {
         // Try to get cached semantics
         Dictionary <string, List <SemanticProperty> > result;
         if (_semanticPropertiesCache.TryGetValue(type, out result))
         {
             return(result);
         }
         result = ModelTypeRegistry.GetPropertySemantics(type);
         _semanticPropertiesCache.Add(type, result);
         return(result);
     }
 }
        /// <summary>
        /// Tries to convert a given generic Topic to a Strongly Typed Topic Model.
        /// </summary>
        /// <param name="genericTopic">The generic Topic to convert.</param>
        /// <param name="ofType">The type of the Strongly Typed Topic Model to convert to. If not specified (or <c>null</c>), the type will be determined from the XHTML.</param>
        /// <returns>The Strongly Typed Topic Model or <c>null</c> if the generic Topic cannot be converted.</returns>
        public EntityModel TryConvertToStronglyTypedTopic(GenericTopic genericTopic, Type ofType = null)
        {
            using (new Tracer(genericTopic, ofType))
            {
                Log.Debug($"Trying to convert {genericTopic} to Strongly Typed Topic Model...");

                IEnumerable <Tuple <string, Type> > registeredTopicTypes = ModelTypeRegistry.GetModelTypesForVocabulary(ViewModel.DitaVocabulary);
                if (registeredTopicTypes == null)
                {
                    Log.Debug("No Strongly Typed Topic Models registered.");
                    return(null);
                }

                XmlElement rootElement = null;
                try
                {
                    rootElement = ParseXhtml(genericTopic);
                }
                catch (Exception ex)
                {
                    Log.Error("Unable to parse generic Topic XHTML.");
                    Log.Debug(genericTopic.TopicBody);
                    Log.Error(ex);
                    return(null);
                }

                Type topicType = ofType;
                if (ofType == null)
                {
                    topicType = DetermineTopicType(rootElement, registeredTopicTypes);
                    if (topicType == null)
                    {
                        Log.Debug("No matching Strongly Typed Topic Model found.");
                        return(null);
                    }
                }

                EntityModel stronglyTypedTopic = BuildStronglyTypedTopic(topicType, rootElement);

                if (stronglyTypedTopic.Id == null)
                {
                    stronglyTypedTopic.Id = genericTopic.Id;
                }

                return(stronglyTypedTopic);
            }
        }
        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;

            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 void MapSemanticProperties(EntityModel stronglyTypedTopic, XmlElement rootElement)
        {
            Type modelType = stronglyTypedTopic.GetType();
            IDictionary <string, List <SemanticProperty> > propertySemanticsMap = ModelTypeRegistry.GetPropertySemantics(modelType);

            foreach (KeyValuePair <string, List <SemanticProperty> > propertySemantics in propertySemanticsMap)
            {
                PropertyInfo             modelProperty      = modelType.GetProperty(propertySemantics.Key);
                List <SemanticProperty>  semanticProperties = propertySemantics.Value;
                IEnumerable <XmlElement> htmlElements       = null;
                foreach (SemanticProperty ditaProperty in semanticProperties.Where(sp => sp.SemanticType.Vocab == ViewModel.DitaVocabulary))
                {
                    string ditaPropertyName = ditaProperty.PropertyName;
                    string propertyXPath    = GetPropertyXPath(ditaPropertyName);
                    Log.Debug($"Trying XPath \"{propertyXPath}\" for property '{modelProperty.Name}'");
                    XmlNodeList xPathResults = rootElement.SelectNodes(propertyXPath);
                    htmlElements = FilterXPathResults(xPathResults, ditaPropertyName);
                    if (htmlElements != null && htmlElements.Any())
                    {
                        break;
                    }
                    Log.Debug($"No XHTML elements found for DITA property '{ditaPropertyName}'.");
                }
                if (htmlElements == null || !htmlElements.Any())
                {
                    Log.Debug($"Unable to map property '{modelProperty.Name}'");
                    continue;
                }
                Log.Debug($"{htmlElements.Count()} XHTML elements found.");

                try
                {
                    object propertyValue = GetPropertyValue(modelProperty.PropertyType, htmlElements);
                    modelProperty.SetValue(stronglyTypedTopic, propertyValue);
                }
                catch (Exception ex)
                {
                    throw new DxaException($"Unable to map property {modelType.Name}.{modelProperty.Name}", ex);
                }
            }
        }
Exemple #12
0
        private static RegionModel CreateRegionModel(RegionModelData regionModelData, Localization localization)
        {
            Common.Models.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   = regionModelData.XpmMetadata;

            if (regionModelData.Regions != null)
            {
                IEnumerable <RegionModel> nestedRegionModels = regionModelData.Regions.Select(data => CreateRegionModel(data, localization));
                result.Regions.UnionWith(nestedRegionModels);
            }

            if (regionModelData.Entities != null)
            {
                foreach (EntityModelData entityModelData in regionModelData.Entities)
                {
                    EntityModel entityModel;
                    try
                    {
                        entityModel = ModelBuilderPipelineR2.CreateEntityModel(entityModelData, null, localization);
                        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);
        }
Exemple #13
0
        /// <summary>
        /// Automatically register all view models for an area. This is done by searching the file system
        /// for all .cshtml files, determining the controller and view names from the path, and using the
        /// BuildManager to determine the model type by compiling the view. Note that if your area contains
        /// a lot of views, this can be a lengthy process and you might be better off explicitly registering
        /// your views with the RegisterViewModel method
        /// </summary>
        protected virtual void RegisterAllViewModels()
        {
            DateTime timer     = DateTime.Now;
            int      viewCount = 0;
            string   baseDir   = AppDomain.CurrentDomain.BaseDirectory;
            string   path      = Path.Combine(baseDir, "Areas", this.AreaName, "Views");

            Log.Debug("Staring to register views for area: {0}", this.AreaName);
            foreach (string file in Directory.GetFiles(path, "*.cshtml", SearchOption.AllDirectories))
            {
                string relativePath = file.Substring(path.Length + 1);
                string virtualPath  = @"~/" + file.Replace(baseDir, string.Empty).Replace("\\", "/");
                int    pos          = relativePath.IndexOf("\\");
                if (pos > 0)
                {
                    string controller = relativePath.Substring(0, pos);
                    string view       = relativePath.Substring(pos + 1);
                    int    extnPos    = view.LastIndexOf(".cshtml");
                    view = view.Substring(0, extnPos);
                    MvcData mvcData = new MvcData {
                        AreaName = this.AreaName, ControllerName = controller, ViewName = view
                    };
                    try
                    {
                        ModelTypeRegistry.RegisterViewModel(mvcData, virtualPath);
                        viewCount++;
                    }
                    catch
                    {
                        //Do nothing - we ignore views that are not strongly typed
                    }
                }
                else
                {
                    Log.Warn("Cannot add view {0} to view model registry as it is not in a {ControllerName} subfolder", file);
                }
            }
            Log.Info("Registered {0} views for area {1} in {2} milliseconds. This startup overhead can be reduced by explicitly registering view using the Sdl.Web.Mvc.Configuration.BaseAreaRegistration.RegisterView() method.", viewCount, this.AreaName, (DateTime.Now - timer).TotalMilliseconds);
        }
        /// <summary>
        /// Determine a Model Type based on semantic mappings (and a given base model type).
        /// </summary>
        /// <param name="baseModelType">The base type as obtained from the View Model.</param>
        /// <returns>The given base Model Type or a subclass if a more specific class can be resolved via semantic mapping.</returns>
        /// <remarks>
        /// This method makes it possible (for example) to let the <see cref="Teaser.Media"/> property get an instance of <see cref="Image"/>
        /// rather than just <see cref="MediaItem"/> (the type of the View Model property).
        /// </remarks>
        public Type GetModelTypeFromSemanticMapping(Type baseModelType)
        {
            Type[]   foundAmbiguousMappings = null;
            string[] semanticTypeNames      = GetSemanticTypeNames();
            foreach (string semanticTypeName in semanticTypeNames)
            {
                IEnumerable <Type> mappedModelTypes = ModelTypeRegistry.GetMappedModelTypes(semanticTypeName);
                if (mappedModelTypes == null)
                {
                    continue;
                }

                Type[] matchingModelTypes = mappedModelTypes.Where(t => baseModelType.IsAssignableFrom(t)).ToArray();
                if (matchingModelTypes.Length == 1)
                {
                    // Exactly one matching model type; return it.
                    return(matchingModelTypes[0]);
                }

                if (matchingModelTypes.Length > 1)
                {
                    // Multiple candidate models types found. Continue scanning; maybe we'll find a unique one for another semantic type.
                    foundAmbiguousMappings = matchingModelTypes;
                }
            }

            if (foundAmbiguousMappings == null)
            {
                Log.Warn("No semantic mapping found between Schema {0} ({1}) and model type '{2}'. Sticking with model type.",
                         Id, String.Join(", ", semanticTypeNames), baseModelType.FullName);
            }
            else
            {
                Log.Warn("Ambiguous semantic mappings found between Schema {0} ({1}) and model type '{2}'. Found types: {3}. Sticking with model type.",
                         Id, String.Join(", ", semanticTypeNames), String.Join(", ", foundAmbiguousMappings.Select(t => t.FullName)), baseModelType.FullName);
            }

            return(baseModelType);
        }
Exemple #15
0
 /// <summary>
 /// Registers a View Model Type without associated View.
 /// </summary>
 private static void RegisterViewModel(Type modelType)
 {
     ModelTypeRegistry.RegisterViewModel(null, modelType);
 }
        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;
            }
        }
Exemple #17
0
 /// <summary>
 /// Registers a View Model without associated View.
 /// </summary>
 /// <param name="modelType">The View Model type.</param>
 /// <remarks>
 /// </remarks>
 protected void RegisterViewModel(Type modelType)
 {
     ModelTypeRegistry.RegisterViewModel(null, modelType);
 }
        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);
        }
Exemple #19
0
 public static void AddViewModelToRegistry(MvcData viewData, string viewPath)
 {
     ModelTypeRegistry.RegisterViewModel(viewData, viewPath);
 }
Exemple #20
0
 public static void AddViewModelToRegistry(MvcData mvcData, Type modelType)
 {
     ModelTypeRegistry.RegisterViewModel(mvcData, modelType);
 }
Exemple #21
0
 protected virtual Type GetViewType(MvcData viewData)
 {
     return(ModelTypeRegistry.GetViewModelType(viewData));
 }