Example #1
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 shortcodeCommands = _shortcode.EnabledLavaCommands ?? new List <string>();

            shortcodeCommands = shortcodeCommands.Where(x => !string.IsNullOrWhiteSpace(x)).ToList();

            if (!shortcodeCommands.Any())
            {
                shortcodeCommands = context.GetEnabledCommands();
            }

            var shortcodeTemplateContext = _engine.NewRenderContext(internalMergeFields, shortcodeCommands);
            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.
            var blockHasContent = residualMarkup.IsNotNullOrWhiteSpace();

            parms.AddOrReplace("blockContentExists", blockHasContent);

            if (blockHasContent)
            {
                parms.AddOrReplace("blockContent", residualMarkup);
            }

            // 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));

            var securityErrorPattern = new Regex(string.Format(Constants.Messages.NotAuthorizedMessage, ".*"));
            var securityErrorMatch   = securityErrorPattern.Match(securityCheckResult.Text);

            // If the security check failed, return the error message.
            if (securityErrorMatch.Success)
            {
                result.Write(securityErrorMatch.Value);

                return;
            }

            // Render the shortcode template in a child scope that includes the shortcode parameters.
            context.EnterChildScope();

            LavaRenderResult results;

            try
            {
                context.SetMergeFields(parms);

                // If the shortcode specifies a set of Lava commands, add these to the context.
                // The set of permitted entity commands is the union of the parent scope and the specific shortcode settings.
                if (_shortcode.EnabledLavaCommands != null)
                {
                    var enabledCommands = context.GetEnabledCommands();
                    foreach (var commandName in _shortcode.EnabledLavaCommands)
                    {
                        if (!enabledCommands.Contains(commandName))
                        {
                            enabledCommands.Add(commandName);
                        }
                    }
                    context.SetEnabledCommands(enabledCommands);
                }

                results = _engine.RenderTemplate(_shortcode.TemplateMarkup, LavaRenderParameters.WithContext(context));
                result.Write(results.Text.Trim());
            }
            finally
            {
                context.ExitChildScope();
            }
        }