Ejemplo n.º 1
0
        /// <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));
        }
Ejemplo n.º 2
0
        /// <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));
        }
Ejemplo n.º 3
0
        /// <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)));
        }
Ejemplo n.º 4
0
        /// <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);
        }
Ejemplo n.º 5
0
        /// <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);
        }
Ejemplo n.º 6
0
        /// <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;
                    }
                }
            }
        }
Ejemplo n.º 7
0
        /// <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);
        }
Ejemplo n.º 8
0
 /// <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);
Ejemplo n.º 9
0
        /// <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);
        }
Ejemplo n.º 10
0
        /// <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);
        }
Ejemplo n.º 11
0
        /// <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);
        }
Ejemplo n.º 12
0
        /// <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());
        }