/// <summary> /// Renders the macro with the specified alias, passing in the specified parameters. /// </summary> /// <param name="alias">The macro alias.</param> /// <param name="parameters">The parameters.</param> /// <param name="content">The content used for macro rendering</param> /// <returns></returns> private IHtmlString RenderMacro(IPublishedContent content, string alias, IDictionary <string, object> parameters) { if (content == null) { throw new ArgumentNullException(nameof(content)); } // TODO: We are doing at ToLower here because for some insane reason the UpdateMacroModel method looks for a lower case match. the whole macro concept needs to be rewritten. //NOTE: the value could have HTML encoded values, so we need to deal with that var macroProps = parameters?.ToDictionary( x => x.Key.ToLowerInvariant(), i => (i.Value is string) ? HttpUtility.HtmlDecode(i.Value.ToString()) : i.Value); string html = _macroRenderer.Render(alias, content, macroProps).GetAsText(); return(new HtmlString(html)); }
// NOT thread-safe over a request because it modifies the // global UmbracoContext.Current.InPreviewMode status. So it // should never execute in // over the same UmbracoContext with // different preview modes. private string RenderRteMacros(string source, bool preview) { var umbracoContext = _umbracoContextAccessor.UmbracoContext; using (umbracoContext.ForcedPreview(preview)) // force for macro rendering { var sb = new StringBuilder(); MacroTagParser.ParseMacros( source, //callback for when text block is found textBlock => sb.Append(textBlock), //callback for when macro syntax is found (macroAlias, macroAttributes) => sb.Append(_macroRenderer.Render( macroAlias, umbracoContext.PublishedRequest?.PublishedContent, //needs to be explicitly casted to Dictionary<string, object> macroAttributes.ConvertTo(x => (string)x, x => x)).GetAsText())); return(sb.ToString()); } }
/// <summary> /// Renders the macro with the specified alias, passing in the specified parameters. /// </summary> /// <param name="alias">The macro alias.</param> /// <param name="parameters">The parameters.</param> /// <param name="content">The content used for macro rendering</param> /// <returns></returns> private IHtmlString RenderMacro(string alias, IDictionary <string, object> parameters, IPublishedContent content) { if (content == null) { throw new ArgumentNullException(nameof(content)); } // TODO: We are doing at ToLower here because for some insane reason the UpdateMacroModel method looks for a lower case match. the whole macro concept needs to be rewritten. //NOTE: the value could have HTML encoded values, so we need to deal with that var macroProps = parameters.ToDictionary( x => x.Key.ToLowerInvariant(), i => (i.Value is string) ? HttpUtility.HtmlDecode(i.Value.ToString()) : i.Value); var macroControl = _macroRenderer.Render(alias, content, macroProps).GetAsControl(); string html; if (macroControl is LiteralControl control) { // no need to execute, we already have text html = control.Text; } else { using (var containerPage = new FormlessPage()) { containerPage.Controls.Add(macroControl); using (var output = new StringWriter()) { // .Execute() does a PushTraceContext/PopTraceContext and writes trace output straight into 'output' // and I do not see how we could wire the trace context to the current context... so it creates dirty // trace output right in the middle of the page. // // The only thing we can do is fully disable trace output while .Execute() runs and restore afterwards // which means trace output is lost if the macro is a control (.ascx or user control) that is invoked // from within Razor -- which makes sense anyway because the control can _not_ run correctly from // within Razor since it will never be inserted into the page pipeline (which may even not exist at all // if we're running MVC). // // I'm sure there's more things that will get lost with this context changing but I guess we'll figure // those out as we go along. One thing we lose is the content type response output. // http://issues.umbraco.org/issue/U4-1599 if it is setup during the macro execution. So // here we'll save the content type response and reset it after execute is called. var contentType = _umbracoContextAccessor.UmbracoContext.HttpContext.Response.ContentType; var traceIsEnabled = containerPage.Trace.IsEnabled; containerPage.Trace.IsEnabled = false; _umbracoContextAccessor.UmbracoContext.HttpContext.Server.Execute(containerPage, output, true); containerPage.Trace.IsEnabled = traceIsEnabled; //reset the content type _umbracoContextAccessor.UmbracoContext.HttpContext.Response.ContentType = contentType; //Now, we need to ensure that local links are parsed html = TemplateUtilities.ParseInternalLinks(output.ToString(), _umbracoContextAccessor.UmbracoContext.UrlProvider); } } } return(new HtmlString(html)); }