/// <summary> /// Render the provided template in a new context with the specified parameters. /// </summary> /// <param name="inputTemplate"></param> /// <param name="parameters">The settings applied to the rendering process.</param> /// <returns> /// A LavaRenderResult object, containing the rendered output of the template or any errors encountered during the rendering process. /// </returns> public static LavaRenderResult RenderTemplate(string inputTemplate, LavaRenderParameters parameters) { if (_engine == null) { return(null); } // If this template is being rendered as part of a web page handler, ensure that the cache key includes a reference // to the associated Rock theme. This is necessary because Lava templates that include references // to theme assets may appear identical, but will render differently if the asset content is different for each theme. // For example, the template "{% include '~~/Assets/Lava/template.lava' %} will produce different output if the content // of the "template.lava" file is different for each theme. var page = HttpContext.Current?.Handler as RockPage; if (page != null) { string cacheKey; if (string.IsNullOrEmpty(parameters.CacheKey)) { cacheKey = _engine.TemplateCacheService.GetCacheKeyForTemplate(inputTemplate); } else { cacheKey = parameters.CacheKey; } parameters.CacheKey = GetWebTemplateCacheKey(cacheKey, page.Site?.Theme); } return(_engine.RenderTemplate(inputTemplate, parameters)); }
/// <summary> /// Render the provided template in a new context with the specified parameters. /// </summary> /// <param name="inputTemplate"></param> /// <param name="parameters">The settings applied to the rendering process.</param> /// <returns> /// A LavaRenderResult object, containing the rendered output of the template or any errors encountered during the rendering process. /// </returns> public static LavaRenderResult RenderTemplate(ILavaTemplate inputTemplate, LavaRenderParameters parameters) { if (_engine == null) { return(null); } return(_engine.RenderTemplate(inputTemplate, parameters)); }
/// <summary> /// Render the provided template in a new context with the specified parameters. /// </summary> /// <param name="inputTemplate"></param> /// <param name="context">The settings applied to the rendering process.</param> /// <returns> /// A LavaRenderResult object, containing the rendered output of the template or any errors encountered during the rendering process. /// </returns> public static LavaRenderResult RenderTemplate(ILavaTemplate inputTemplate, ILavaRenderContext context) { if (_engine == null) { return(null); } return(RenderTemplate(inputTemplate, LavaRenderParameters.WithContext(context))); }
/// <summary> /// Returns a new object with the same properties as the current object. /// </summary> /// <returns></returns> public LavaRenderParameters Clone() { var clone = new LavaRenderParameters(); clone.CacheKey = CacheKey; clone.Context = Context; clone.Culture = Culture; clone.ExceptionHandlingStrategy = ExceptionHandlingStrategy; clone.ShouldEncodeStringsAsXml = ShouldEncodeStringsAsXml; clone.TimeZone = TimeZone; return(clone); }
/// <summary> /// Render the provided template in a new context with the specified merge fields. /// </summary> /// <param name="inputTemplate"></param> /// <param name="mergeFields">The collection of merge fields to be added to the context used to render the template.</param> /// <returns> /// The rendered output of the template. /// If the template is invalid, returns an error message or an empty string according to the current ExceptionHandlingStrategy setting. /// </returns> /// <remarks>This render method is compatible with the legacy DotLiquid implementation.</remarks> public static LavaRenderResult RenderTemplate(string inputTemplate, IDictionary <string, object> mergeFields) { LavaRenderResult result; if (_engine == null && _rockLiquidIsEnabled) { // If a Lava engine is not initialized, fall back to legacy DotLiquid. // This allows developers to use a single method for basic template rendering that is backward-compatible with the RockLiquid implementation. result = new LavaRenderResult(); result.Text = inputTemplate.ResolveMergeFields(mergeFields); return(result); } result = RenderTemplate(inputTemplate, LavaRenderParameters.WithContext(_engine.NewRenderContext(mergeFields))); return(result); }
/// <summary> /// Parses the element attributes markup to collate parameter settings for the shortcode. /// </summary> /// <param name="elementAttributesMarkup">The markup.</param> /// <param name="context">The context.</param> /// <returns></returns> private void SetParametersFromElementAttributes(Dictionary <string, object> parameters, string elementAttributesMarkup, ILavaRenderContext context) { // Resolve any Lava merge fields in the element attributes markup. var resolvedMarkup = _engine.RenderTemplate(elementAttributesMarkup, LavaRenderParameters.WithContext(context)); var markupItems = Regex.Matches(resolvedMarkup.Text, @"(\S*?:'[^']+')") .Cast <Match>() .Select(m => m.Value) .ToList(); foreach (var item in markupItems) { var itemParts = item.ToString().Split(new char[] { ':' }, 2); if (itemParts.Length > 1) { parameters.AddOrReplace(itemParts[0].Trim().ToLower(), itemParts[1].Trim().Substring(1, itemParts[1].Length - 2)); } } // OK, now let's look for any passed variables ala: name:variable var variableTokens = Regex.Matches(resolvedMarkup.Text, @"\w*:\w+") .Cast <Match>() .Select(m => m.Value) .ToList(); foreach (var item in variableTokens) { var itemParts = item.Trim().Split(new char[] { ':' }, 2); if (itemParts.Length > 1) { var scopeKey = itemParts[1].Trim(); var scopeObject = context.GetMergeField(scopeKey, null); if (scopeObject != null) { parameters.AddOrReplace(itemParts[0].Trim().ToLower(), scopeObject); break; } } } }
/// <summary> /// Render the provided template in a new context with the specified merge fields. /// </summary> /// <param name="inputTemplate"></param> /// <param name="mergeFields">The collection of merge fields to be added to the context used to render the template.</param> /// <returns> /// The rendered output of the template. /// If the template is invalid, returns an error message or an empty string according to the current ExceptionHandlingStrategy setting. /// </returns> public static LavaRenderResult RenderTemplate(ILavaTemplate inputTemplate, IDictionary <string, object> mergeFields) { var result = RenderTemplate(inputTemplate, LavaRenderParameters.WithContext(_engine.NewRenderContext(mergeFields))); return(result); }
/// <summary> /// Override this method to render the Lava template using the underlying rendering engine. /// </summary> /// <param name="inputTemplate"></param> /// <param name="parameters"></param> /// <returns></returns> protected abstract LavaRenderResult OnRenderTemplate(ILavaTemplate inputTemplate, LavaRenderParameters parameters);
/// <summary> /// Render the provided template using the specified parameters. /// </summary> /// <param name="inputTemplate"></param> /// <param name="parameters"></param> /// <returns> /// The result of the render operation. /// If the template is invalid, the Text property may contain an error message or an empty string according to the current ExceptionHandlingStrategy setting. /// </returns> public LavaRenderResult RenderTemplate(ILavaTemplate template, LavaRenderParameters parameters) { parameters = parameters ?? new LavaRenderParameters(); LavaRenderResult result; LavaRenderParameters callParameters; try { if (parameters == null) { callParameters = new LavaRenderParameters(); } else if (parameters.Context == null) { callParameters = parameters.Clone(); callParameters.Context = NewRenderContext(); } else { if (parameters.Context.GetType() == typeof(LavaRenderContext)) { callParameters = parameters.Clone(); // Convert the default context to an engine-specific implementation. var engineContext = NewRenderContext(); engineContext.SetInternalFields(parameters.Context.GetInternalFields()); engineContext.SetMergeFields(parameters.Context.GetMergeFields()); engineContext.SetEnabledCommands(parameters.Context.GetEnabledCommands()); // Create a copy of the parameters to ensure the input parameter remains unchanged. callParameters.Context = engineContext; } else { callParameters = parameters; } } result = OnRenderTemplate(template, callParameters); if (result.Error != null) { result.Error = GetLavaRenderException(result.Error); } } catch (LavaInterruptException) { // This exception is intentionally thrown by a component to halt the render process. result = new LavaRenderResult(); result.Text = string.Empty; } catch (Exception ex) { if (ex is ThreadAbortException) { // Ignore this exception, the calling thread is terminating and no result is required. return(null); } result = new LavaRenderResult(); var lre = GetLavaRenderException(ex); string message; ProcessException(lre, parameters.ExceptionHandlingStrategy, out message); result.Error = lre; result.Text = message; } return(result); }
/// <summary> /// Render the provided template using the specified parameters. /// </summary> /// <param name="inputTemplate"></param> /// <param name="parameters"></param> /// <returns> /// The result of the render operation. /// If the template is invalid, the Text property may contain an error message or an empty string according to the current ExceptionHandlingStrategy setting. /// </returns> public LavaRenderResult RenderTemplate(string inputTemplate, LavaRenderParameters parameters) { ILavaTemplate template; LavaRenderParameters activeParameters; // Copy the render parameters so they can be altered without affecting the input object. if (parameters == null) { activeParameters = new LavaRenderParameters(); } else { activeParameters = parameters.Clone(); } var renderResult = new LavaRenderResult(); var exceptionStrategy = activeParameters.ExceptionHandlingStrategy ?? this.ExceptionHandlingStrategy; try { if (_cacheService != null) { template = _cacheService.GetOrAddTemplate(this, inputTemplate, activeParameters.CacheKey); } else { template = null; } bool isParsed = (template != null); if (!isParsed) { var parseResult = ParseTemplate(inputTemplate); if (parseResult.HasErrors) { throw parseResult.Error; } template = parseResult.Template; } if (activeParameters.Context == null) { activeParameters.Context = NewRenderContext(); } renderResult = RenderTemplate(template, activeParameters); } catch (Exception ex) { var lre = GetLavaRenderException(ex, inputTemplate); string message; if (ex is System.Threading.ThreadAbortException) { // If the requesting thread terminated unexpectedly, return an empty string. // This may happen, for example, when a Lava template triggers a page redirect in a web application. message = "{Request aborted}"; } else { ProcessException(lre, exceptionStrategy, out message); } renderResult.Error = ex; renderResult.Text = message; } return(renderResult); }
/// <summary> /// Render the provided template in the specified context. /// </summary> /// <param name="inputTemplate"></param> /// <param name="context"></param> /// <returns> /// The rendered output of the template. /// If the template is invalid, returns an error message or an empty string according to the current ExceptionHandlingStrategy setting. /// </returns> public LavaRenderResult RenderTemplate(string inputTemplate, ILavaDataDictionary mergeFields) { var result = RenderTemplate(inputTemplate, LavaRenderParameters.WithContext(this.NewRenderContext(mergeFields))); return(result); }
/// <summary> /// Renders the specified context. /// </summary> /// <param name="context">The context.</param> /// <param name="result">The result.</param> public override void OnRender(ILavaRenderContext context, TextWriter result) { if (_shortcode == null) { result.Write($"An error occurred while processing the {0} shortcode.", _tagName); } // Get the parameters and default values defined by the shortcode, then apply the parameters that have been specified in the shortcode tag attributes // and those that are stored in the current render context. var parms = new Dictionary <string, object>(); foreach (var shortcodeParm in _shortcode.Parameters) { parms.AddOrReplace(shortcodeParm.Key, shortcodeParm.Value); } SetParametersFromElementAttributes(parms, _elementAttributesMarkup, context); // Set a unique id for the shortcode. parms.AddOrReplace("uniqueid", "id-" + Guid.NewGuid().ToString()); // Apply the merge fields in the block context. var internalMergeFields = context.GetMergeFields(); foreach (var item in parms) { internalMergeFields.AddOrReplace(item.Key, item.Value); } // Add parameters for tracking the recursion depth. int currentRecursionDepth = 0; if (parms.ContainsKey("RecursionDepth")) { currentRecursionDepth = parms["RecursionDepth"].ToString().AsInteger() + 1; if (currentRecursionDepth > _maxRecursionDepth) { result.Write("A recursive loop was detected and processing of this shortcode has stopped."); return; } } parms.AddOrReplace("RecursionDepth", currentRecursionDepth); // Resolve the merge fields in the shortcode template in a separate context, using the set of merge fields that have been modified by the shortcode parameters. // Apply the set of enabled commands specified by the shortcode definition, or those enabled for the current context if none are defined by the shortcode. // The resulting content is a shortcode template that is ready to be processed to resolve its child elements. var enabledCommands = _shortcode.EnabledLavaCommands ?? new List <string>(); enabledCommands = enabledCommands.Where(x => !string.IsNullOrWhiteSpace(x)).ToList(); if (!enabledCommands.Any()) { enabledCommands = context.GetEnabledCommands(); } var shortcodeTemplateContext = _engine.NewRenderContext(internalMergeFields, enabledCommands); var blockMarkupRenderResult = _engine.RenderTemplate(_blockMarkup.ToString(), LavaRenderParameters.WithContext(shortcodeTemplateContext)); var shortcodeTemplateMarkup = blockMarkupRenderResult.Text; // Extract child elements from the shortcode template content. // One or more child elements can be added to a shortcode block using the syntax "[[ <childElementName> <paramName1>:value1 <paramName2>:value2 ... ]] ... [[ end<childElementName> ]]", // where <childElementName> is a shortcode-specific tag name, <param1> is a shortcode parameter name, and <value1> is the parameter value. // Child elements are grouped by <childElementName>, and each collection is passed as a separate parameter to the shortcode template // using the variable name "<childElementNameItems>". The first element of the array is also added using the variable name "<childElementName>". // Parameters declared on child elements can be referenced in the shortcode template as <childElementName>.<paramName>. Dictionary <string, object> childElements; var residualMarkup = ExtractShortcodeBlockChildElements(shortcodeTemplateMarkup, out childElements); // Add the collections of child to the set of parameters that will be passed to the shortcode template. foreach (var item in childElements) { parms.AddOrReplace(item.Key, item.Value); } // Set context variables related to the block content so they can be referenced by the shortcode template. if (residualMarkup.IsNotNullOrWhiteSpace()) { // JME (7/23/2019) Commented out the two lines below and substituted the line after to allow for better // processing of the block content. Testing was done on all existing shortcodes but leaving // this code in place in case a future edge case is found. Could/should remove this in the future. // Regex rgx = new Regex( @"{{\s*blockContent\s*}}", RegexOptions.IgnoreCase ); // lavaTemplate = rgx.Replace( lavaTemplate, blockMarkup ); parms.AddOrReplace("blockContent", residualMarkup); parms.AddOrReplace("blockContentExists", true); } else { parms.AddOrReplace("blockContentExists", false); } // Now ensure there aren't any entity commands in the block that are not allowed. // This is necessary because the shortcode may be configured to allow more entities for processing // than the source block, template, action, etc. permits. var securityCheckResult = _engine.RenderTemplate(residualMarkup, LavaRenderParameters.WithContext(context)); Regex securityErrorPattern = new Regex(string.Format(Constants.Messages.NotAuthorizedMessage, ".*")); Match securityErrorMatch = securityErrorPattern.Match(securityCheckResult.Text); // If the security check failed, return the error message. if (securityErrorMatch.Success) { result.Write(securityErrorMatch.Value); return; } // Merge the shortcode template in a new context, using the parameters and security allowed by the shortcode. var shortcodeContext = _engine.NewRenderContext(parms); // If the shortcode specifies a set of enabled Lava commands, set these for the current context. // If not, use the commands enabled for the current context. if (_shortcode.EnabledLavaCommands != null && _shortcode.EnabledLavaCommands.Any()) { shortcodeContext.SetEnabledCommands(_shortcode.EnabledLavaCommands); } else { shortcodeContext.SetEnabledCommands(context.GetEnabledCommands()); } var results = _engine.RenderTemplate(_shortcode.TemplateMarkup, LavaRenderParameters.WithContext(shortcodeContext)); result.Write(results.Text.Trim()); }