Represents data about the Model, View and Controller
        /// <summary>
        /// Read additional properties from XHTML element, and set view name in MvcData.
        /// </summary>
        /// <param name="xhtmlElement">XHTML element</param>
        public override void ReadFromXhtmlElement(XmlElement xhtmlElement)
        {
            base.ReadFromXhtmlElement(xhtmlElement);

            AlternateText = xhtmlElement.GetAttribute("alt");
            MvcData = new MvcData("Core:Entity:Image");
        }
Exemple #2
0
        /// <summary>
        /// Registers a View Model and associated View.
        /// </summary>
        /// <param name="viewData">The data for the View to register or <c>null</c> if only the Model Type is to be registered.</param>
        /// <param name="modelType">The model Type used by the View.</param>
        public static void RegisterViewModel(MvcData viewData, Type modelType)
        {
            using (new Tracer(viewData, modelType))
            {
                lock (_viewToModelTypeMapping)
                {
                    if (viewData != null)
                    {
                        if (_viewToModelTypeMapping.ContainsKey(viewData))
                        {
                            Log.Warn("View '{0}' registered multiple times.", viewData);
                            return;
                        }
                        _viewToModelTypeMapping.Add(viewData, modelType);
                    }

                    // Obtain a lock on _modelTypeToSemanticInfoMapping too to prevent a race with GetSemanticInfo
                    lock (_modelTypeToSemanticInfoMapping)
                    {
                        if (!_modelTypeToSemanticInfoMapping.ContainsKey(modelType))
                        {
                            RegisterModelType(modelType);
                        }
                    }
                }
            }
        }
        /// <summary>
        /// Registers a View Model mapping by compiling a given view file and obtaining its model type.
        /// </summary>
        /// <param name="viewData">The data for the View to register.</param>
        /// <param name="viewVirtualPath">The (virtual) path to the View file.</param>
        public static void RegisterViewModel(MvcData viewData, string viewVirtualPath)
        {
            using (new Tracer(viewData, viewVirtualPath))
            {
                lock (_viewToModelTypeMapping)
                {
                    if (_viewToModelTypeMapping.ContainsKey(viewData))
                    {
                        Log.Warn("View '{0}' registered multiple times. Virtual Path: '{1}'", viewData, viewVirtualPath);
                        return;
                    }

                    try
                    {
                        Type compiledViewType = BuildManager.GetCompiledType(viewVirtualPath);
                        if (!compiledViewType.BaseType.IsGenericType)
                        {
                            throw new DxaException("View is not strongly typed. Please ensure you use the @model directive.");
                        }
                        RegisterViewModel(viewData, compiledViewType.BaseType.GetGenericArguments()[0]);
                    }
                    catch (Exception ex)
                    {
                        throw new DxaException(string.Format("Error occurred while compiling View '{0}'", viewVirtualPath), ex);
                    }
                }
            }
        }
Exemple #4
0
        /// <summary>
        /// Determines whether the specified object is equal to the current object.
        /// </summary>
        /// <param name="obj">The object to compare with the current object. </param>
        public override bool Equals(object obj)
        {
            MvcData other = obj as MvcData;

            if (other == null)
            {
                return(false);
            }

            // NOTE: RegionName and RegionAreaName are not included in equality check (these merely carry some additional info).
            if ((ControllerName != other.ControllerName) ||
                (ControllerAreaName != other.ControllerAreaName) ||
                (ActionName != other.ActionName) ||
                (ViewName != other.ViewName) ||
                (AreaName != other.AreaName))
            {
                return(false);
            }

            if (RouteValues == null)
            {
                return(other.RouteValues == null);
            }
            return(RouteValues.SequenceEqual(other.RouteValues));
        }
Exemple #5
0
        /// <summary>
        /// Registers a View Model mapping by compiling a given view file and obtaining its model type.
        /// </summary>
        /// <param name="viewData">The data for the View to register.</param>
        /// <param name="viewVirtualPath">The (virtual) path to the View file.</param>
        public static void RegisterViewModel(MvcData viewData, string viewVirtualPath)
        {
            using (new Tracer(viewData, viewVirtualPath))
            {
                lock (_viewToModelTypeMapping)
                {
                    if (_viewToModelTypeMapping.ContainsKey(viewData))
                    {
                        Log.Warn("View '{0}' registered multiple times. Virtual Path: '{1}'", viewData, viewVirtualPath);
                        return;
                    }

                    try
                    {
                        Type compiledViewType = BuildManager.GetCompiledType(viewVirtualPath);
                        if (!compiledViewType.BaseType.IsGenericType)
                        {
                            throw new DxaException("View is not strongly typed. Please ensure you use the @model directive.");
                        }
                        RegisterViewModel(viewData, compiledViewType.BaseType.GetGenericArguments()[0]);
                    }
                    catch (Exception ex)
                    {
                        throw new DxaException(string.Format("Error occurred while compiling View '{0}'", viewVirtualPath), ex);
                    }
                }
            }
        }
        /// <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);
            }
        }
 /// <summary>
 /// Initializes a new <see cref="RegionModel"/> instance for an empty/non-existing Region.
 /// </summary>
 /// <param name="name">The name of the Region.</param>
 /// <param name="qualifiedViewName">The qualified name of the View to use to render the Region. Format: format AreaName:ControllerName:ViewName.</param>
 public RegionModel(string name, string qualifiedViewName) 
     : base(name)
 {
     MvcData = new MvcData(qualifiedViewName)
     {
         ActionName = "Region"
     };
 }
Exemple #8
0
        /// <summary>
        /// Serves as a hash function for a particular type.
        /// </summary>
        /// <returns>
        /// A hash code for the current Entity Model.
        /// </returns>
        public override int GetHashCode()
        {
            int h0 = Id?.GetHashCode() ?? base.GetHashCode();
            int h1 = HtmlClasses?.GetHashCode() ?? 0;
            int h2 = MvcData?.GetHashCode() ?? 0;

            return(Hash.CombineHashCodes(h0, h1, h2));
        }
Exemple #9
0
 /// <summary>
 /// Initializes a new <see cref="RegionModel"/> instance for an empty/non-existing Region.
 /// </summary>
 /// <param name="name">The name of the Region.</param>
 /// <param name="qualifiedViewName">The qualified name of the View to use to render the Region. Format: format AreaName:ControllerName:ViewName.</param>
 public RegionModel(string name, string qualifiedViewName)
     : this(name)
 {
     MvcData = new MvcData(qualifiedViewName)
     {
         ActionName = "Region"
     };
 }
        /// <summary>
        /// Read additional properties from XHTML element, and set view name in MvcData.
        /// </summary>
        /// <param name="xhtmlElement">XHTML element</param>
        public override void ReadFromXhtmlElement(XmlElement xhtmlElement)
        {
            // initialize base
            base.ReadFromXhtmlElement(xhtmlElement);

            YouTubeId = xhtmlElement.GetAttribute("data-youTubeId");
            Headline = xhtmlElement.GetAttribute("data-headline");
            MvcData = new MvcData("Core:Entity:YouTubeVideo");
        }
        /// <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 #12
0
        /// <summary>
        /// Post-processes the Page Model constructed by the <see cref="DefaultModelBuilder"/>.
        /// </summary>
        /// <remarks>
        /// This implementation relies on the <see cref="DefaultModelBuilder"/> already having constructed Region Models of type <see cref="SmartTargetRegion"/>.
        /// We "upgrade" the Page Model to type <see cref="SmartTargetPageModel"/> and populate the ST Regions <see cref="SmartTargetPromotion"/> Entities.
        /// </remarks>
        public void BuildPageModel(ref PageModel pageModel, IPage page, IEnumerable <IPage> includes, ILocalization localization)
        {
            using (new Tracer(pageModel, page, includes, localization))
            {
                if ((pageModel == null) || !pageModel.Regions.OfType <SmartTargetRegion>().Any())
                {
                    Log.Debug("No SmartTarget Regions on Page.");
                    return;
                }

                if ((page?.PageTemplate?.MetadataFields == null) || !page.PageTemplate.MetadataFields.ContainsKey("regions"))
                {
                    Log.Debug("No Regions metadata found.");
                    return;
                }

                // "Upgrade" the PageModel to a SmartTargetPageModel, so we can store AllowDuplicates in the Page Model.
                SmartTargetPageModel smartTargetPageModel = new SmartTargetPageModel(pageModel)
                {
                    AllowDuplicates = GetAllowDuplicatesOnSamePage(page.PageTemplate, localization),
                    NoCache         = true // Don't cache the Page Model, because its contents are dynamic.
                };
                pageModel = smartTargetPageModel;

                // Set SmartTargetRegionModel.MaxItem based on the Region Metadata in the Page Template.
                foreach (IFieldSet smartTargetRegionField in page.PageTemplate.MetadataFields["regions"].EmbeddedValues)
                {
                    string regionName;
                    IField regionNameField;
                    if (smartTargetRegionField.TryGetValue("name", out regionNameField) && !string.IsNullOrEmpty(regionNameField.Value))
                    {
                        regionName = regionNameField.Value;
                    }
                    else
                    {
                        regionName = new MvcData(smartTargetRegionField["view"].Value).ViewName;
                    }

                    SmartTargetRegion smartTargetRegion = smartTargetPageModel.Regions[regionName] as SmartTargetRegion;
                    if (smartTargetRegion != null)
                    {
                        int    maxItems = 100; // Default
                        IField maxItemsField;
                        if (smartTargetRegionField.TryGetValue("maxItems", out maxItemsField))
                        {
                            maxItems = Convert.ToInt32(maxItemsField.NumericValues[0]);
                        }
                        smartTargetRegion.MaxItems = maxItems;
                    }
                }

                PopulateSmartTargetRegions(smartTargetPageModel, localization);
            }
        }
 /// <summary>
 /// Initializes a new <see cref="MvcData"/> instance which is a copy of another.
 /// </summary>
 /// <param name="other">The other <see cref="MvcData"/> to copy.</param>
 public MvcData(MvcData other)
 {
     ControllerName = other.ControllerName;
     ControllerAreaName = other.ControllerAreaName;
     ActionName = other.ActionName;
     ViewName = other.ViewName;
     AreaName = other.AreaName;
     RegionName = other.RegionName;
     RegionAreaName = other.RegionAreaName;
     if (other.RouteValues != null)
     {
         RouteValues = new Dictionary<string, string>(other.RouteValues);
     }
 }
Exemple #14
0
 /// <summary>
 /// Initializes a new <see cref="MvcData"/> instance which is a copy of another.
 /// </summary>
 /// <param name="other">The other <see cref="MvcData"/> to copy.</param>
 public MvcData(MvcData other)
 {
     ControllerName     = other.ControllerName;
     ControllerAreaName = other.ControllerAreaName;
     ActionName         = other.ActionName;
     ViewName           = other.ViewName;
     AreaName           = other.AreaName;
     RegionName         = other.RegionName;
     RegionAreaName     = other.RegionAreaName;
     if (other.RouteValues != null)
     {
         RouteValues = new Dictionary <string, string>(other.RouteValues);
     }
 }
 /// <summary>
 /// Get the View Model Type for a given View.
 /// </summary>
 /// <param name="viewData">The data for the View.</param>
 /// <returns>The View Model Type.</returns>
 public static Type GetViewModelType(MvcData viewData)
 {
     Type modelType;
     MvcData bareMvcData = new MvcData
     {
         AreaName = viewData.AreaName,
         ControllerName = viewData.ControllerName,
         ViewName = viewData.ViewName
     };
     if (!_viewToModelTypeMapping.TryGetValue(bareMvcData, out modelType))
     {
         throw new DxaException(
             string.Format("No View Model registered for View '{0}'. Check that you have registered this View in the '{1}' area registration.", viewData, viewData.AreaName)
             );
     }
     return modelType;
 }
Exemple #16
0
        /// <summary>
        /// Get the View Model Type for a given View.
        /// </summary>
        /// <param name="viewData">The data for the View.</param>
        /// <returns>The View Model Type.</returns>
        public static Type GetViewModelType(MvcData viewData)
        {
            Type    modelType;
            MvcData bareMvcData = new MvcData
            {
                AreaName       = viewData.AreaName,
                ControllerName = viewData.ControllerName,
                ViewName       = viewData.ViewName
            };

            if (!_viewToModelTypeMapping.TryGetValue(bareMvcData, out modelType))
            {
                throw new DxaException(
                          string.Format("No View Model registered for View '{0}'. Check that you have registered this View in the '{1}' area registration.", viewData, viewData.AreaName)
                          );
            }
            return(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;

            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);
        }
        /// <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>
        protected 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 { AreaName = AreaName, ControllerName = controllerName, ViewName = viewName };
            ModelTypeRegistry.RegisterViewModel(mvcData, modelType);
        }
        /// <summary>
        /// Registers a View Model and associated View.
        /// </summary>
        /// <param name="viewData">The data for the View to register or <c>null</c> if only the Model Type is to be registered.</param>
        /// <param name="modelType">The model Type used by the View.</param>
        public static void RegisterViewModel(MvcData viewData, Type modelType)
        {
            using (new Tracer(viewData, modelType))
            {
                lock (_viewToModelTypeMapping)
                {
                    if (viewData != null)
                    {
                        if (_viewToModelTypeMapping.ContainsKey(viewData))
                        {
                            Log.Warn("View '{0}' registered multiple times.", viewData);
                            return;
                        }
                        _viewToModelTypeMapping.Add(viewData, modelType);
                    }

                    if (!_modelTypeToSemanticInfoMapping.ContainsKey(modelType))
                    {
                        RegisterModelType(modelType);
                    }
                }
            }
        }
 /// <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);
 }
Exemple #21
0
 /// <summary>
 /// Adds a View->View Model Type mapping to the view model registry
 /// </summary>
 /// <param name="viewData">The View Data used to determine the registry key and model type</param>
 /// <param name="viewPath">The path to the view</param>
 public static void AddViewModelToRegistry(MvcData viewData, string viewPath)
 {
     lock (ViewRegistryLock)
     {
         var key = String.Format("{0}:{1}", viewData.AreaName, viewData.ViewName);
         if (!ViewModelRegistry.ContainsKey(key))
         {
             try
             {
                 Type type = BuildManager.GetCompiledType(viewPath);
                 if (type.BaseType.IsGenericType)
                 {
                     ViewModelRegistry.Add(key, type.BaseType.GetGenericArguments()[0]);
                 }
                 else
                 {
                     Exception ex = new Exception(String.Format("View {0} is not strongly typed. Please ensure you use the @model directive", viewPath));
                     Log.Error(ex);
                     throw ex;
                 }
             }
             catch (Exception ex)
             {
                 Exception e = new Exception(String.Format("Error adding view model to registry using view path {0}", viewPath), ex);
                 Log.Error(e);
                 throw e;
             }
         }
     }
 }
        /// <summary>
        /// Post-processes the Page Model constructed by the <see cref="DefaultModelBuilder"/>.
        /// </summary>
        /// <remarks>
        /// This implementation relies on the <see cref="DefaultModelBuilder"/> already having constructed Region Models of type <see cref="SmartTargetRegion"/>.
        /// We "upgrade" the Page Model to type <see cref="SmartTargetPageModel"/> and populate the ST Regions <see cref="SmartTargetPromotion"/> Entities.
        /// </remarks>
        public void BuildPageModel(ref PageModel pageModel, IPage page, IEnumerable<IPage> includes, Localization localization)
        {
            using (new Tracer(pageModel, page, includes, localization))
            {
                if (pageModel == null || !pageModel.Regions.OfType<SmartTargetRegion>().Any())
                {
                    Log.Debug("No SmartTarget Regions on Page.");
                    return;
                }

                if (page == null || page.PageTemplate == null || page.PageTemplate.MetadataFields == null || !page.PageTemplate.MetadataFields.ContainsKey("regions"))
                {
                    Log.Debug("No Regions metadata found.");
                    return;
                }

                // "Upgrade" the PageModel to a SmartTargetPageModel, so we can store AllowDuplicates in the Page Model.
                SmartTargetPageModel smartTargetPageModel = new SmartTargetPageModel(pageModel)
                {
                    AllowDuplicates = GetAllowDuplicatesOnSamePage(page.PageTemplate, localization),
                    NoCache = true // Don't cache the Page Model, because its contents are dynamic.
                };
                pageModel = smartTargetPageModel;

                // Set SmartTargetRegionModel.MaxItem based on the Region Metadata in the Page Template.
                foreach (IFieldSet smartTargetRegionField in page.PageTemplate.MetadataFields["regions"].EmbeddedValues)
                {
                    string regionName;
                    IField regionNameField;
                    if (smartTargetRegionField.TryGetValue("name", out regionNameField) && !String.IsNullOrEmpty(regionNameField.Value))
                    {
                        regionName = regionNameField.Value;
                    }
                    else
                    {
                        regionName = new MvcData(smartTargetRegionField["view"].Value).ViewName;
                    }

                    SmartTargetRegion smartTargetRegion = smartTargetPageModel.Regions[regionName] as SmartTargetRegion;
                    if (smartTargetRegion != null)
                    {
                        int maxItems = 100; // Default
                        IField maxItemsField;
                        if (smartTargetRegionField.TryGetValue("maxItems", out maxItemsField))
                        {
                            maxItems = Convert.ToInt32(maxItemsField.NumericValues[0]);
                        }
                        smartTargetRegion.MaxItems = maxItems;
                    }
                }

                // Execute a ST Query for all SmartTargetRegions on the Page.
                ResultSet resultSet = ExecuteSmartTargetQuery(smartTargetPageModel, localization);
                Log.Debug("SmartTarget query returned {0} Promotions.", resultSet.Promotions.Count);

                string promotionViewName = localization.GetConfigValue(PromotionViewNameConfig);
                if (String.IsNullOrEmpty(promotionViewName))
                {
                    Log.Warn("No View name for SmartTarget Promotions is configured on CM-side ({0})", PromotionViewNameConfig);
                    promotionViewName = "SmartTarget:Entity:Promotion";
                }
                Log.Debug("Using Promotion View '{0}'", promotionViewName);

                // TODO: we shouldn't access HttpContext in a Model Builder.
                HttpContext httpContext = HttpContext.Current;
                if (httpContext == null)
                {
                    throw new DxaException("HttpContext is not available.");
                }

                List<string> itemsAlreadyOnPage = new List<string>();
                ExperimentCookies existingExperimentCookies = CookieProcessor.GetExperimentCookies(httpContext.Request);
                ExperimentCookies newExperimentCookies = new ExperimentCookies();

                // Filter the Promotions for each SmartTargetRegion
                foreach (SmartTargetRegion smartTargetRegion in smartTargetPageModel.Regions.OfType<SmartTargetRegion>())
                {
                    string regionName = smartTargetRegion.Name;

                    List<string> itemsOutputInRegion = new List<string>();
                    ExperimentDimensions experimentDimensions;
                    List<Promotion> promotions = new List<Promotion>(resultSet.Promotions);
                    ResultSet.FilterPromotions(promotions, regionName, smartTargetRegion.MaxItems, smartTargetPageModel.AllowDuplicates, itemsOutputInRegion,
                            itemsAlreadyOnPage, ref existingExperimentCookies, ref newExperimentCookies,
                            out experimentDimensions);

                    if (experimentDimensions != null)
                    {
                        // The SmartTarget API doesn't set all ExperimentDimensions properties, but they are required by the ExperimentTrackingHandler (see CRQ-1667).
                        experimentDimensions.PublicationId = string.Format("tcm:0-{0}-1", localization.LocalizationId);
                        experimentDimensions.PageId =  string.Format("tcm:{0}-{1}-64", localization.LocalizationId, smartTargetPageModel.Id);
                        experimentDimensions.Region = smartTargetRegion.Name;
                    }

                    if (localization.IsStaging)
                    {
                        // The SmartTarget API provides the entire XPM markup tag; put it in XpmMetadata["Query"]. See SmartTargetRegion.GetStartQueryXpmMarkup.
                        smartTargetRegion.XpmMetadata = new Dictionary<string, object>
                        {
                            {"Query", ResultSet.GetExperienceManagerMarkup(smartTargetRegion.Name, smartTargetRegion.MaxItems, promotions)}
                        };
                    }

                    // Create SmartTargetPromotion Entity Models for visible Promotions in the current SmartTargetRegion.
                    // It seems that ResultSet.FilterPromotions doesn't really filter on Region name, so we do post-filtering here.
                    foreach (Promotion promotion in promotions.Where(promotion => promotion.Visible && promotion.Regions.Contains(regionName)))
                    {
                        SmartTargetPromotion smartTargetPromotion = CreatePromotionEntity(promotion, promotionViewName, smartTargetRegion.Name, localization, experimentDimensions);

                        if (!smartTargetRegion.HasSmartTargetContent)
                        {
                            // Discard any fallback content coming from Content Manager
                            smartTargetRegion.Entities.Clear();
                            smartTargetRegion.HasSmartTargetContent = true;
                        }

                        smartTargetRegion.Entities.Add(smartTargetPromotion);
                    }
                }

                if (newExperimentCookies.Count > 0)
                {
                    smartTargetPageModel.ExperimentCookies = newExperimentCookies;
                }
            }
        }
        /// <summary>
        /// Renders a given Entity Model.
        /// </summary>
        /// <param name="htmlHelper">The HtmlHelper instance on which the extension method operates.</param>
        /// <param name="entity">The Entity to render.</param>
        /// <param name="viewName">The (qualified) name of the View used to render the entity. This overrides the View set in <see cref="EntityModel.MvcData"/>.</param>
        /// <param name="containerSize">The size (in grid column units) of the containing element.</param>
        /// <returns>The rendered HTML or an empty string if <paramref name="entity"/> is <c>null</c>.</returns>
        public static MvcHtmlString DxaEntity(this HtmlHelper htmlHelper, EntityModel entity, string viewName, int containerSize = 0)
        {
            MvcData mvcDataOverride = new MvcData(viewName);
            MvcData orginalMvcData = entity.MvcData;
            MvcData tempMvcData = new MvcData(orginalMvcData)
            {
                AreaName = mvcDataOverride.AreaName,
                ViewName = mvcDataOverride.ViewName
            };

            try
            {
                entity.MvcData = tempMvcData;
                return htmlHelper.DxaEntity(entity, containerSize);
            }
            finally
            {
                entity.MvcData = orginalMvcData;
            }
        }
        /// <summary>
        /// Renders the current (Include) Page as a Region.
        /// </summary>
        /// <param name="htmlHelper">The HtmlHelper instance on which the extension method operates.</param>
        /// <returns>The rendered HTML.</returns>
        public static MvcHtmlString DxaRegion(this HtmlHelper htmlHelper)
        {
            using (new Tracer(htmlHelper))
            {
                PageModel pageModel = (PageModel)htmlHelper.ViewData.Model;

                // Create a new Region Model which reflects the Page Model
                string regionName = pageModel.Title;
                MvcData mvcData = new MvcData
                {
                    ViewName = regionName,
                    AreaName = SiteConfiguration.GetDefaultModuleName(),
                    ControllerName = SiteConfiguration.GetRegionController(),
                    ControllerAreaName = SiteConfiguration.GetDefaultModuleName(),
                    ActionName = SiteConfiguration.GetRegionAction()
                };

                RegionModel regionModel = new RegionModel(regionName) { MvcData = mvcData };
                regionModel.Regions.UnionWith(pageModel.Regions);

                return htmlHelper.DxaRegion(regionModel);
            }
        }
        /// <summary>
        /// Determine MVC data such as view, controller and area name from a Component Presentation, Region or Page
        /// </summary>
        /// <param name="data">The component presentation, region or page object</param>
        /// <returns></returns>
        public MvcData ResolveMvcData(object data)
        {
            var res = new MvcData();
            if (data is IComponentPresentation)
            {
                var cp = data as IComponentPresentation;
                var template = cp.ComponentTemplate;
                var viewName = Regex.Replace(template.Title, @"\[.*\]|\s", String.Empty);

                if (template.MetadataFields != null)
                {
                    if (template.MetadataFields.ContainsKey("view"))
                    {
                        viewName = template.MetadataFields["view"].Value;
                    }
                }
                res = BuildViewData(viewName);
                //Defaults
                res.ControllerName = SiteConfiguration.GetEntityController();
                res.ControllerAreaName = SiteConfiguration.GetDefaultModuleName();
                res.ActionName = SiteConfiguration.GetEntityAction();
                res.RouteValues = new Dictionary<string, string>();

                if (template.MetadataFields !=null)
                {
                    if (template.MetadataFields.ContainsKey("controller"))
                    {
                        var bits = template.MetadataFields["controller"].Value.Split(':');
                        if (bits.Length > 1)
                        {
                            res.ControllerName = bits[1];
                            res.ControllerAreaName = bits[0];
                        }
                        else
                        {
                            res.ControllerName = bits[0];
                        }
                    }
                    if (template.MetadataFields.ContainsKey("regionView"))
                    {
                        var bits = template.MetadataFields["regionView"].Value.Split(':');
                        if (bits.Length > 1)
                        {
                            res.RegionName = bits[1];
                            res.RegionAreaName = bits[0];
                        }
                        else
                        {
                            res.RegionName = bits[0];
                            res.RegionAreaName = SiteConfiguration.GetDefaultModuleName();
                        }
                    }
                    if (template.MetadataFields.ContainsKey("action"))
                    {
                        res.ActionName = template.MetadataFields["action"].Value;
                    }
                    if (template.MetadataFields.ContainsKey("routeValues"))
                    {
                        var bits = template.MetadataFields["routeValues"].Value.Split(',');
                        foreach (string bit in bits)
                        {
                            var parameter = bit.Trim().Split(':');
                            if (parameter.Length > 1 && !res.RouteValues.ContainsKey(parameter[0]))
                            {
                                res.RouteValues.Add(parameter[0],parameter[1]);
                            }
                        }
                    }
                }
            }
            else if (data is IPage)
            {
                var page = data as IPage;
                var viewName = page.PageTemplate.Title.RemoveSpaces();
                if (page.PageTemplate.MetadataFields != null)
                {
                    if (page.PageTemplate.MetadataFields.ContainsKey("view"))
                    {
                        viewName = page.PageTemplate.MetadataFields["view"].Value;
                    }
                }
                res = BuildViewData(viewName);
                res.ControllerName = SiteConfiguration.GetPageController();
                res.ControllerAreaName = SiteConfiguration.GetDefaultModuleName();
                res.ActionName = SiteConfiguration.GetPageController();
            }
            else if (data is IRegion)
            {
                var region = data as IRegion;
                var viewName = region.Name.RemoveSpaces();
                res = BuildViewData(viewName);
                res.ControllerName = SiteConfiguration.GetRegionController();
                res.ActionName = SiteConfiguration.GetRegionAction();
                res.ControllerAreaName = SiteConfiguration.GetDefaultModuleName();
                res.AreaName = region.Module;
            }
            return res;
        }
        /// <summary>
        /// Creates predefined Regions from Page Template metadata.
        /// </summary>
        private static void CreatePredefinedRegions(RegionModelSet regions, IPageTemplate pageTemplate)
        {
            IFieldSet ptMetadataFields = pageTemplate.MetadataFields;
            IField regionsField;
            if (ptMetadataFields == null || !ptMetadataFields.TryGetValue("regions", out regionsField)) // TODO: "region" instead of "regions"
            {
                Log.Debug("No Region metadata defined for Page Template '{0}'.", pageTemplate.Id);
                return;
            }

            foreach (IFieldSet regionMetadataFields in regionsField.EmbeddedValues)
            {
                IField regionViewNameField;
                if (!regionMetadataFields.TryGetValue("view", out regionViewNameField))
                {
                    Log.Warn("Region metadata without 'view' field encountered in metadata of Page Template '{0}'.", pageTemplate.Id);
                    continue;
                }

                MvcData regionMvcData = new MvcData(regionViewNameField.Value);
                InitializeRegionMvcData(regionMvcData);
                RegionModel regionModel = CreateRegionModel(regionMvcData);
                regions.Add(regionModel);
            }
        }
        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);
        }
 /// <summary>
 /// Creates a Region Model of class <see cref="RegionModel"/> or a subclass associated with the given Region View.
 /// </summary>
 private static RegionModel CreateRegionModel(MvcData regionMvcData)
 {
     Type regionModelType = ModelTypeRegistry.GetViewModelType(regionMvcData);
     RegionModel regionModel = (RegionModel) Activator.CreateInstance(regionModelType, regionMvcData.ViewName);
     regionModel.MvcData = regionMvcData;
     return regionModel;
 }
        /// <summary>
        /// Renders a given Entity Model.
        /// </summary>
        /// <param name="htmlHelper">The HtmlHelper instance on which the extension method operates.</param>
        /// <param name="entity">The Entity to render.</param>
        /// <param name="viewName">The (qualified) name of the View used to render the entity. This overrides the View set in <see cref="EntityModel.MvcData"/>.</param>
        /// <param name="containerSize">The size (in grid column units) of the containing element.</param>
        /// <returns>The rendered HTML or an empty string if <paramref name="entity"/> is <c>null</c>.</returns>
        public static MvcHtmlString DxaEntity(this HtmlHelper htmlHelper, EntityModel entity, string viewName, int containerSize = 0)
        {
            MvcData mvcDataOverride = new MvcData(viewName);
            entity.MvcData.AreaName = mvcDataOverride.AreaName;
            entity.MvcData.ViewName = mvcDataOverride.ViewName;

            return htmlHelper.DxaEntity(entity, containerSize);
        }
 /// <summary>
 /// Registers a View Model and associated View.
 /// </summary>
 /// <param name="viewName">The name of the View.</param>
 /// <param name="modelType">The view model type</param>
 /// <param name="controllerName">The controller name (eg Search)</param>
 protected void RegisterViewModel(string viewName, Type modelType, string controllerName = "Entity")
 {
     MvcData mvcData = new MvcData { AreaName = AreaName, ControllerName = controllerName, ViewName = viewName };
     ModelTypeRegistry.RegisterViewModel(mvcData, modelType);
 }
        private static RegionModel GetRegionFromIncludePage(IPage page)
        {
            string regionName = page.Title;

            MvcData regionMvcData = new MvcData(regionName);
            InitializeRegionMvcData(regionMvcData);

            return new RegionModel(regionName)
            {
                MvcData = regionMvcData,
                XpmMetadata = new Dictionary<string, string>
                {
                    {RegionModel.IncludedFromPageIdXpmMetadataKey, page.Id},
                    {RegionModel.IncludedFromPageTitleXpmMetadataKey, page.Title},
                    {RegionModel.IncludedFromPageFileNameXpmMetadataKey, page.Filename}
                }
            };
        }
        /// <summary>
        /// Set view name in MvcData.
        /// </summary>
        /// <param name="xhtmlElement">XHTML element</param>
        public override void ReadFromXhtmlElement(XmlElement xhtmlElement)
        {
            base.ReadFromXhtmlElement(xhtmlElement);

            MvcData = new MvcData("Core:Entity:Download");
        }
        private static MvcData GetRegionMvcData(IComponentPresentation cp)
        {
            string regionName = null;
            string module = SiteConfiguration.GetDefaultModuleName(); //Default module
            if (cp.ComponentTemplate.MetadataFields != null)
            {
                if (cp.ComponentTemplate.MetadataFields.ContainsKey("regionView"))
                {
                    regionName = DetermineRegionViewNameAndModule(cp.ComponentTemplate.MetadataFields["regionView"].Value, out module);
                }
            }

            // fallback if no meta - use the CT title
            if (regionName == null)
            {
                Match match = Regex.Match(cp.ComponentTemplate.Title, @".*?\[(.*?)\]");
                if (match.Success)
                {
                    regionName = DetermineRegionViewNameAndModule(match.Groups[1].Value, out module);
                }
            }

            regionName = regionName ?? "Main"; // default region name

            MvcData regionMvcData = new MvcData { AreaName = module, ViewName = regionName };
            InitializeRegionMvcData(regionMvcData);
            return regionMvcData;
        }
 private static void InitializeRegionMvcData(MvcData regionMvcData)
 {
     if (String.IsNullOrEmpty(regionMvcData.ControllerName))
     {
         regionMvcData.ControllerName = SiteConfiguration.GetRegionController();
         regionMvcData.ControllerAreaName = SiteConfiguration.GetDefaultModuleName();
     }
     else if (String.IsNullOrEmpty(regionMvcData.ControllerAreaName))
     {
         regionMvcData.ControllerAreaName = regionMvcData.AreaName;
     }
     regionMvcData.ActionName = SiteConfiguration.GetRegionAction();
 }