/// <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="Localization"/>.</param> public void BuildPageModel(ref PageModel pageModel, PageModelData pageModelData, bool includePageRegions, ILocalization localization) { using (new Tracer(pageModel, pageModelData, includePageRegions, localization)) { if ((pageModel == null) || !pageModel.Regions.OfType <SmartTargetRegion>().Any()) { Log.Debug("No SmartTarget Regions on Page."); return; } // "Upgrade" the PageModel to a SmartTargetPageModel, so we can store AllowDuplicates in the Page Model. SmartTargetPageModel smartTargetPageModel = new SmartTargetPageModel(pageModel) { AllowDuplicates = GetAllowDuplicatesOnSamePage(pageModelData, localization), NoCache = true // Don't cache the Page Model, because its contents are dynamic. }; pageModel = smartTargetPageModel; // Set SmartTargetRegionModel.MaxItem based on the Region Metadata foreach (SmartTargetRegion smartTargetRegion in pageModel.Regions.OfType <SmartTargetRegion>()) { string regionName = smartTargetRegion.Name; RegionModelData regionModelData = pageModelData.Regions.First(r => r.Name == regionName); int maxItems = 100; // Default if ((regionModelData.Metadata != null) && regionModelData.Metadata.ContainsKey("maxItems")) { maxItems = GetInt(regionModelData.Metadata["maxItems"]); } smartTargetRegion.MaxItems = maxItems; } PopulateSmartTargetRegions(smartTargetPageModel, localization); } }
/// <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); } }
private static ResultSet ExecuteSmartTargetQuery(SmartTargetPageModel smartTargetPageModel, Localization localization) { using (new Tracer(smartTargetPageModel, localization)) { TcmUri pageUri = new TcmUri(String.Format("tcm:{0}-{1}-64", localization.LocalizationId, smartTargetPageModel.Id)); TcmUri publicationUri = new TcmUri(0, pageUri.PublicationId, 1); ClaimStore claimStore = AmbientDataContext.CurrentClaimStore; string triggers = AmbientDataHelper.GetTriggers(claimStore); QueryBuilder queryBuilder = new QueryBuilder(); queryBuilder.Parse(triggers); queryBuilder.AddCriteria(new PublicationCriteria(publicationUri)); queryBuilder.AddCriteria(new PageCriteria(pageUri)); // Adding all the page regions to the query for having only 1 query a page foreach (SmartTargetRegion region in smartTargetPageModel.Regions.OfType <SmartTargetRegion>()) { queryBuilder.AddCriteria(new RegionCriteria(region.Name)); } return(queryBuilder.Execute()); } }
/// <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) }; 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 = smartTargetRegionField.ContainsKey("maxItems") ? Convert.ToInt32(smartTargetRegionField["maxItems"].Value) : 100; 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); List <string> itemsAlreadyOnPage = new List <string>(); ExperimentCookies existingExperimentCookies = CookieProcessor.GetExperimentCookies(HttpContext.Current.Request); // TODO: we shouldn't access HttpContext in a Model Builder. // Filter the Promotions for each SmartTargetRegion foreach (SmartTargetRegion smartTargetRegion in smartTargetPageModel.Regions.OfType <SmartTargetRegion>()) { string regionName = smartTargetRegion.Name; List <string> itemsOutputInRegion = new List <string>(); ExperimentCookies newExperimentCookies = new ExperimentCookies(); 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 (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.Region.Contains(regionName))) { SmartTargetPromotion smartTargetPromotion = CreatePromotionEntity(promotion, promotionViewName, smartTargetRegion.Name, localization); if (!smartTargetRegion.HasSmartTargetContent) { // Discard any fallback content coming from Content Manager smartTargetRegion.Entities.Clear(); smartTargetRegion.HasSmartTargetContent = true; } smartTargetRegion.Entities.Add(smartTargetPromotion); } } } }
private void PopulateSmartTargetRegions(SmartTargetPageModel smartTargetPageModel, ILocalization localization) { // Execute a ST Query for all SmartTargetRegions on the Page. ResultSet resultSet = ExecuteSmartTargetQuery(smartTargetPageModel, localization); Log.Debug($"SmartTarget query returned {resultSet.Promotions.Count} Promotions."); string promotionViewName = localization.GetConfigValue(PromotionViewNameConfig); if (String.IsNullOrEmpty(promotionViewName)) { Log.Warn($"No View name for SmartTarget Promotions is configured on CM-side ({PromotionViewNameConfig})"); promotionViewName = "SmartTarget:Entity:Promotion"; } Log.Debug($"Using Promotion View '{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 = localization.GetCmUri(); experimentDimensions.PageId = localization.GetCmUri(smartTargetPageModel.Id, (int)ItemType.Page); experimentDimensions.Region = smartTargetRegion.Name; } if (localization.IsXpmEnabled) { // 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; } }