public static Task <IHtmlContent> RenderContentZone(this IViewComponentHelper component, AgilityPage page, string zoneName) { try { HttpContext currentContext = AgilityContext.HttpContext; String websiteName = AgilityContext.WebsiteName; if (page == null) { if (AgilityContext.IsTemplatePreview) { //TODO render the template preview //var templates = from cs in AgilityContext.CurrentPageTemplateInPreview.ContentSections // where string.Equals(cs.Name, zoneName, StringComparison.CurrentCultureIgnoreCase) // orderby cs.XModuleOrder // select cs; //TextWriter tw = helper.ViewContext.Writer; //foreach (var cs in templates) //{ // tw.Write(string.Format("<div class=\"AgilityContentSectionDefinition\"><div class=\"ContentSectionTitle\">{0}</div></div>", cs.Name)); //} } return(Task.Run <IHtmlContent>(() => { return new HtmlString("TODO: implement template preview"); })); } //regular page rendering var sections = from cs in page.ContentSections where string.Equals(cs.Name, zoneName, StringComparison.CurrentCultureIgnoreCase) orderby cs.ModuleOrder select cs; List <Task <string> > sectionTasks = new List <Task <string> >(); DateTime dtStart = DateTime.Now; foreach (var cs in sections) { //only continue if we have a content ref name... if (string.IsNullOrEmpty(cs.ContentReferenceName)) { continue; } //keep track of the various way this module can be rendered List <ModuleRender> renders = new List <ModuleRender>() { new ModuleRender() { ContentReferenceName = cs.ContentReferenceName } }; //if this content zone is part of a Module AB test Experiment if (cs.ExperimentID > 0) { var lst = BaseCache.GetExperiments(AgilityContext.WebsiteName); AgilityContentServer.AgilityExperiment experiment = lst.GetExperiment(cs.ExperimentID); if (experiment != null) { foreach (var variant in experiment.Variants.Where(v => !string.IsNullOrWhiteSpace(v.ContentReferenceName))) { renders.Add(new ModuleRender() { ContentReferenceName = variant.ContentReferenceName, Variant = variant }); } } } foreach (ModuleRender moduleRender in renders) { try { string contentReferenceName = moduleRender.ContentReferenceName; Agility.Web.AgilityContentServer.AgilityContent moduleContent = BaseCache.GetContent(contentReferenceName, AgilityContext.LanguageCode, websiteName); if (moduleContent == null || moduleContent.DataSet == null || moduleContent.DataSet.Tables["ContentItems"] == null || moduleContent.DataSet.Tables["ContentItems"].Rows.Count == 0) { continue; } DataRowView drv = moduleContent.DataSet.Tables["ContentItems"].DefaultView[0]; int moduleContentID = -1; int moduleVersionID = -1; if (!int.TryParse($"{drv["ContentID"]}", out moduleContentID)) { moduleContentID = -1; } if (!int.TryParse($"{drv["VersionID"]}", out moduleVersionID)) { moduleVersionID = -1; } Agility.Web.AgilityContentServer.AgilityModule module = BaseCache.GetModule(cs.ModuleID, websiteName); if (module == null) { continue; } object model = new AgilityModuleModel() { ModuleContentName = contentReferenceName, ModuleProperties = drv, LanguageCode = AgilityContext.LanguageCode }; string viewComponentName = null; string agilityCodeRefName = null; using (StringReader sr = new StringReader(module.XmlSchema)) { DataSet ds = new DataSet(); ds.ReadXmlSchema(sr); viewComponentName = ds.ExtendedProperties["ViewComponent"] as string; agilityCodeRefName = ds.ExtendedProperties["AgilityCodeRefName"] as string; } if (!string.IsNullOrEmpty(viewComponentName)) { #region *** VIEW COMPONENTS ** var viewComponentFactory = HtmlHelperViewExtensions.GetServiceOrFail <IViewComponentSelector>(AgilityContext.HttpContext); var componentDesc = viewComponentFactory.SelectComponent(viewComponentName); if (componentDesc == null) { throw new ApplicationException(string.Format("The view component {0} was not found.", viewComponentName)); } MethodInfo method = componentDesc.MethodInfo; if (method == null) { throw new ApplicationException(string.Format("The component invoke method was not found in the component {0}", viewComponentName)); } ParameterInfo[] paramAry = method.GetParameters(); if (paramAry.Length > 0) { ParameterInfo paramInfo = paramAry[0]; Type paramType = paramInfo.ParameterType; if (paramType.IsSubclassOf(typeof(AgilityContentItem)) || paramType == typeof(AgilityContentItem)) { ConstructorInfo ci = paramType.GetConstructor(System.Type.EmptyTypes); if (ci == null) { throw new ApplicationException(string.Format("No default constructor found for type {0}", paramType.Name)); } AgilityContentItem moduleItem = ci.Invoke(new object[0]) as AgilityContentItem; moduleItem.DataRow = drv.Row; moduleItem.LanguageCode = AgilityContext.LanguageCode; moduleItem.ReferenceName = contentReferenceName; moduleItem.ContentID = moduleContentID; moduleItem.VersionID = moduleVersionID; try { Task <IHtmlContent> task = component.InvokeAsync(viewComponentName, moduleItem); moduleRender.RenderTask = task; moduleRender.ContentID = moduleContentID; } catch (Exception ex) { moduleRender.PreRenderedContent = new HtmlString($"<p>Error rendering Component {viewComponentName}</p><pre>{ex}</pre>"); moduleRender.ContentID = moduleContentID; } } else { throw new ApplicationException(string.Format("The component invoke method parameter was not of type AgilityContent in the component {0}", viewComponentName)); } #endregion } } else if (!string.IsNullOrEmpty(agilityCodeRefName)) { #region *** Agility Inline Code *** DataView dv = Data.GetContentView(AgilityDynamicCodeFile.REFNAME_AgilityModuleCodeTemplates, AgilityDynamicCodeFile.LANGUAGECODE_CODE); string filter = string.Format("ReferenceName = '{0}' AND Visible = true", agilityCodeRefName); DataRow[] rows = dv.Table.Select(filter); if (rows.Length > 0) { string modulePath = $"~/Views/{rows[0]["VersionID"]}/DynamicAgilityCode/{AgilityDynamicCodeFile.REFNAME_AgilityModuleCodeTemplates}/{agilityCodeRefName}.cshtml"; AgilityContentItem moduleItem = new AgilityContentItem(); moduleItem.DataRow = drv.Row; moduleItem.LanguageCode = AgilityContext.LanguageCode; moduleItem.ReferenceName = contentReferenceName; moduleItem.ContentID = moduleContentID; moduleItem.VersionID = moduleVersionID; //moduleItem.InlineCodePath = modulePath; try { Task <IHtmlContent> task = component.InvokeAsync("AgilityInlineCode", new { inlineCodePath = modulePath, module = moduleItem }); moduleRender.RenderTask = task; moduleRender.ContentID = moduleContentID; } catch (Exception ex) { moduleRender.PreRenderedContent = new HtmlString($"<p>Error rendering Inline Code</p><pre>{ex}</pre>"); moduleRender.ContentID = moduleContentID; } } else { moduleRender.PreRenderedContent = new HtmlString($"<p>Error rendering Inline Code</p>"); moduleRender.ContentID = moduleContentID; } #endregion } else if (!string.IsNullOrEmpty(module.ControlPath)) { #region *** Control Path - Partial View *** string className = GetClassName(module.ReferenceName ?? module.Name); string typeName = string.Format("Module_{0}", className); Type paramType = Agility.Web.Utils.FileUtils.GetTypeFromReflection(null, typeName); if (paramType.IsSubclassOf(typeof(AgilityContentItem)) || paramType == typeof(AgilityContentItem)) { ConstructorInfo ci = paramType.GetConstructor(System.Type.EmptyTypes); if (ci == null) { throw new ApplicationException(string.Format("No default constructor found for type {0}", paramType.Name)); } AgilityContentItem moduleItem = ci.Invoke(new object[0]) as AgilityContentItem; moduleItem.DataRow = drv.Row; moduleItem.LanguageCode = AgilityContext.LanguageCode; moduleItem.ReferenceName = contentReferenceName; moduleItem.ContentID = moduleContentID; moduleItem.VersionID = moduleVersionID; try { Task <IHtmlContent> task = component.InvokeAsync("AgilityPartialView", new { partialViewPath = module.ControlPath, module = moduleItem }); moduleRender.RenderTask = task; moduleRender.ContentID = moduleContentID; } catch (Exception ex) { moduleRender.PreRenderedContent = new HtmlString($"<p>Error rendering Partial View at {module.ControlPath}</p><pre>{ex}</pre>"); moduleRender.ContentID = moduleContentID; } } else { throw new ApplicationException(string.Format("The component invoke method parameter was not of type AgilityContent in the component {0}", viewComponentName)); } #endregion } else if (!string.IsNullOrEmpty(module.Markup)) { if (module.Markup.StartsWith("@")) { #region *** Inline Razor Markup *** AgilityContentItem item = new AgilityContentItem(); item.DataRow = drv.Row; item.LanguageCode = AgilityContext.LanguageCode; item.ReferenceName = contentReferenceName; item.ContentID = moduleContentID; item.VersionID = moduleVersionID; model = item; string viewPath = string.Format("~/Views/DynamicAgilityModule/MVC/{0}/{1}.cshtml", AgilityContext.CurrentMode, module.ID); try { Task <IHtmlContent> task = component.InvokeAsync("AgilityInlineCode", new { inlineCodePath = viewPath, module = item }); moduleRender.RenderTask = task; moduleRender.ContentID = moduleContentID; } catch (Exception ex) { moduleRender.PreRenderedContent = new HtmlString($"<p>Error rendering Dynamic Agility Module</p><pre>{ex}</pre>"); moduleRender.ContentID = moduleContentID; } #endregion } } } catch (Exception ex) { if (Current.Settings.DevelopmentMode) { moduleRender.PreRenderedContent = new HtmlString($"<div>Could not output zone {cs.Name}</div><div>{ex.ToString().Replace("\n", "<br/>")}</div>"); } else { moduleRender.PreRenderedContent = new HtmlString($"<!-- Could not output zone {cs.Name} - See web log -->"); Agility.Web.Tracing.WebTrace.WriteException(ex); } } } Task <string> sectionTask = Task <string> .Run(() => { AgilityContext.HttpContext = currentContext; var rendersToOutput = renders.Where(r => r.RenderTask != null || r.PreRenderedContent != null).ToList(); return(AgilityHelpers.RenderModuleHtml(rendersToOutput, cs)); }); sectionTasks.Add(sectionTask); } Task <IHtmlContent> retTask = Task.Run <IHtmlContent>(() => { AgilityContext.HttpContext = currentContext; try { Task.WaitAll(sectionTasks.ToArray()); } catch { } using (StringWriter htmlStringWriter = new StringWriter()) { foreach (var t in sectionTasks) { if (t.IsFaulted) { Agility.Web.Tracing.WebTrace.WriteException(t.Exception, $"Error rendering module in zone {zoneName} - {t.AsyncState}"); } else { htmlStringWriter.Write(t.Result); } } TimeSpan ts = DateTime.Now - dtStart; if (ts.TotalSeconds > 1) { string renderTimeMessage = string.Format("Content Zone: {0} - Render Time: {1:F2} seconds.", zoneName, ts.TotalMilliseconds / 1000); Agility.Web.Tracing.WebTrace.WriteVerboseLine(renderTimeMessage); htmlStringWriter.Write(string.Format("<!-- {0} -->", renderTimeMessage)); } return(new HtmlString(htmlStringWriter.ToString())); } }); return(retTask); } catch (Exception ex) { string errHtml = null; string msg = string.Format("Could not output content zone {0}.", zoneName); if (ex is InvalidOperationException && ex.Message.Contains("No route")) { msg += @" This error is usually caused by a missing route in your Global.asax.cs. Ensure the following route is defined after the normal Agility route.\n routes.MapRoute(""Default"", ""{controller}/{action}/{id}"",\n new { controller = """", action = """", id = """" }\n );"; } if (Current.Settings.DevelopmentMode) { errHtml = ex.ToString().Replace("\n", "<br/>"); } else { errHtml = msg; } Agility.Web.Tracing.WebTrace.WriteException(ex, msg); return(Task.Run <IHtmlContent>(() => { return new HtmlString(errHtml); })); } }
public async Task <HtmlString> InvokeAsync() { AgilityContext.HttpContext = HttpContext; AgilityPage currentPage = AgilityContext.Page; if (currentPage == null) { return(null); } StringBuilder sb = new StringBuilder(Environment.NewLine); string scriptTopGlobal; string scriptBottomGlobal; string scriptBottomPage = null; //output the id of any page a/b test experiments var experiments = BaseCache.GetExperiments(AgilityContext.WebsiteName); var experiment = experiments.GetForPage(currentPage.ID); if (experiment != null) { if (experiment.Variants != null && experiment.Variants.Any(v => !string.IsNullOrWhiteSpace(v.URL))) { //PAGE REDIRECT EXPERIMENTS ONLY AgilityContext.ExperimentKeys.Add(experiment.Key); sb.AppendLine("<script type='text/javascript'>"); //get the winner if (experiment.WinningVariant != null) { sb.AppendFormat("window.AgilityExperimentWinningVariant = {{ experiment: '{0}' variant: {1}, url: '{2}' }};", experiment.Key, experiment.WinningVariant.ID, AgilityHelpers.ResolveUrl(experiment.WinningVariant.URL) ); } else { sb.Append("window.AgilityExperimentVariants = ["); foreach (var v in experiment.Variants) { sb.AppendFormat("{{ experiment: '{0}', variant: {1}, url: '{2}' }},", experiment.Key, v.ID, AgilityHelpers.ResolveUrl(v.URL) ); } sb.AppendLine("];"); sb.AppendLine("</script>"); } } } else if (currentPage.ServerPage.ExperimentIDs != null) { foreach (int exId in currentPage.ServerPage.ExperimentIDs) { experiment = experiments.GetExperiment(exId); if (experiment != null) { AgilityContext.ExperimentKeys.Add(experiment.Key); } } } //add the Javascript tracking stuff if (currentPage.IncludeInStatsTracking) { //global script if (!string.IsNullOrEmpty(AgilityContext.Domain.StatsTrackingScript)) { scriptTopGlobal = AgilityContext.Domain.StatsTrackingScript; if (scriptTopGlobal.IndexOf(AgilityHelpers.GLOBAL_SCRIPT_SEPARATOR) != -1) { scriptBottomGlobal = scriptTopGlobal.Substring(scriptTopGlobal.IndexOf(AgilityHelpers.GLOBAL_SCRIPT_SEPARATOR) + AgilityHelpers.GLOBAL_SCRIPT_SEPARATOR.Length); scriptTopGlobal = scriptTopGlobal.Substring(0, scriptTopGlobal.IndexOf(AgilityHelpers.GLOBAL_SCRIPT_SEPARATOR)); } if (!string.IsNullOrEmpty(scriptTopGlobal)) { sb.Append(scriptTopGlobal); sb.Append(Environment.NewLine); } } } string scriptTopPage = null; //custom script for this page if (!string.IsNullOrEmpty(currentPage.CustomAnalyticsScript)) { scriptTopPage = currentPage.CustomAnalyticsScript; if (scriptTopPage.IndexOf(AgilityHelpers.GLOBAL_SCRIPT_SEPARATOR) != -1) { scriptBottomPage = scriptTopPage.Substring(scriptTopPage.IndexOf(AgilityHelpers.GLOBAL_SCRIPT_SEPARATOR) + AgilityHelpers.GLOBAL_SCRIPT_SEPARATOR.Length); scriptTopPage = scriptTopPage.Substring(0, scriptTopPage.IndexOf(AgilityHelpers.GLOBAL_SCRIPT_SEPARATOR)); } if (!string.IsNullOrEmpty(scriptTopPage)) { sb.Append(scriptTopPage); sb.Append(Environment.NewLine); } } return(new HtmlString(sb.ToString())); }
internal static string RenderModuleHtml(List <ModuleRender> renders, ContentSection cs) { using (StringWriter writer = new StringWriter()) { if (renders == null || renders.Count == 0) { writer.Write(string.Format("<!-- No Output from Module {0} -->", cs.ContentReferenceName)); return(writer.ToString()); } //wait for the renders.. var renderTasks = renders.Where(r => r.RenderTask != null).Select(r => r.RenderTask).ToArray(); if (renderTasks.Length > 0) { Task.WaitAll(renderTasks); } AgilityContentServer.AgilityExperiment experiment = null; if (cs.ExperimentID > 0) { var lst = BaseCache.GetExperiments(AgilityContext.WebsiteName); experiment = lst.GetExperiment(cs.ExperimentID); } HtmlEncoder htmlEncoder = HtmlEncoder.Default; if (experiment == null) { foreach (var render in renders) { if (render != null) { try { render.PreRenderedContent.WriteTo(writer, htmlEncoder); } catch (Exception ex) { Agility.Web.Tracing.WebTrace.WriteException(ex, $"Error rendering Module {render.ContentReferenceName}"); } } } return(writer.ToString()); } else { AgilityContext.ExperimentKeys.Add(experiment.Key); if (experiment.Variants.Length > 0) { //MODULE AB TEST WITH VARIANTS writer.Write(string.Format("<div id=\"agility-abtest-container-{0}\" data-agility-experiment=\"{0}\" data-agility-variants=\"{1}\"></div>", experiment.Key, experiment.Variants.Length)); int index = 0; foreach (var render in renders) { var html = render.PreRenderedContent; int moduleContentItemID = render.ContentID; //remove this content id from the "auto" list of content ids on this page... AgilityContext.LoadedContentItemIDs.Remove(moduleContentItemID); int variantID = 0; if (render.Variant != null) { variantID = render.Variant.ID; } //wrap the variant in a script writer.Write(string.Format("<script id=\"agility-abtest-variant-{0}-{1}\" type=\"text/html\" data-agility-content-id=\"{2}\" data-agility-experiment=\"{1}\" data-agility-variant=\"{3}\">", experiment.Key, index, moduleContentItemID, variantID) ); html.WriteTo(writer, htmlEncoder); writer.Write("</script>"); if (render.Variant == null) { //output the "control" (default) in a noscript for SEO / accessibility writer.Write("<noscript>"); html.WriteTo(writer, htmlEncoder); writer.Write("</noscript>"); } index++; } } else { foreach (var render in renders) { var html = render.PreRenderedContent; int moduleContentItemID = render.ContentID; //CONTENT EXPERIMENT - there were only 1 render... //wrap the module in a script and a noscript tag... writer.Write(string.Format("<div id=\"agility-experiment-container-{1}\" data-agility-content-id=\"{0}\" data-agility-experiment=\"{1}\"></div>", moduleContentItemID, experiment.Key)); writer.Write(string.Format("<script id=\"agility-experiment-root-{1}\" type=\"text/html\" data-agility-content-id=\"{0}\" data-agility-experiment=\"{1}\">", moduleContentItemID, experiment.Key)); html.WriteTo(writer, htmlEncoder); writer.Write("</script>"); writer.Write("<noscript>"); html.WriteTo(writer, htmlEncoder); writer.Write("</noscript>"); } } } return(writer.ToString()); } }