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