/// <summary> /// Parses the attributes of the Lava element tag to extract the parameter settings. /// Any merge fields in the attributes markup are resolved before the settings are extracted. /// </summary> /// <param name="elementAttributesMarkup">The markup.</param> /// <param name="context">The context.</param> /// <returns></returns> public static Dictionary <string, string> GetElementAttributes(string elementAttributesMarkup, ILavaRenderContext context = null) { // First, resolve any Lava merge fields that exist in the element attributes markup. if (context != null) { elementAttributesMarkup = elementAttributesMarkup.ResolveMergeFields(context.GetMergeFields()); } // Get the set of parameters using variations of the following pattern: // param1:'value1 with spaces' param2:value2_without_spaces param3:'value3 with spaces' var parms = new Dictionary <string, string>(); var markupItems = Regex.Matches(elementAttributesMarkup, @"(\S*?:('[^']+'|[\\d.]+|[\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) { if (itemParts[1].Trim()[0] == '\'') { parms.AddOrReplace(itemParts[0].Trim(), itemParts[1].Trim().Substring(1, itemParts[1].Length - 2)); } else { parms.AddOrReplace(itemParts[0].Trim(), itemParts[1].Trim()); } } } return(parms); }
/// <summary> /// Parses the markup. /// </summary> /// <param name="markup">The markup.</param> /// <param name="context">The context.</param> /// <returns></returns> private Dictionary <string, string> ParseMarkup(string markup, ILavaRenderContext context) { // first run lava across the inputted markup var internalMergeFields = context.GetMergeFields(); var resolvedMarkup = markup.ResolveMergeFields(internalMergeFields); var parms = new Dictionary <string, string>(); parms.Add("return", "results"); parms.Add("statement", "select"); var markupItems = Regex.Matches(resolvedMarkup, @"(\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) { parms.AddOrReplace(itemParts[0].Trim().ToLower(), itemParts[1].Trim().Substring(1, itemParts[1].Length - 2)); } } return(parms); }
/// <summary> /// Parses the markup. /// </summary> /// <param name="markup">The markup.</param> /// <param name="context">The context.</param> /// <returns></returns> private Dictionary <string, string> ParseMarkup(string markup, ILavaRenderContext context) { // first run lava across the inputted markup var internalMergeFields = context.GetMergeFields(); var resolvedMarkup = markup.ResolveMergeFields(internalMergeFields); var parms = new Dictionary <string, string>(); parms.Add("iterator", "results"); parms.Add("searchtype", "wildcard"); var markupItems = Regex.Matches(resolvedMarkup, @"(\S*?:'[^']+')") .Cast <Match>() .Select(m => m.Value) .ToList(); if (markupItems.Count == 0) { throw new Exception("No parameters were found in your command. The syntax for a parameter is parmName:'' (note that you must use single quotes)."); } foreach (var item in markupItems) { var itemParts = item.ToString().Split(new char[] { ':' }, 2); if (itemParts.Length > 1) { parms.AddOrReplace(itemParts[0].Trim().ToLower(), itemParts[1].Trim().Substring(1, itemParts[1].Length - 2)); } } return(parms); }
/// <summary> /// Parses the markup. /// </summary> /// <param name="markup">The markup.</param> /// <param name="context">The context.</param> /// <returns></returns> private Dictionary <string, string> ParseMarkup(string markup, ILavaRenderContext context) { // first run lava across the inputted markup var internalMergeFields = context.GetMergeFields(); var resolvedMarkup = markup.ResolveMergeFields(internalMergeFields); var parms = new Dictionary <string, string>(); parms.Add("method", "GET"); parms.Add("return", "results"); parms.Add("basicauth", ""); parms.Add("parameters", ""); parms.Add("headers", ""); parms.Add("responsecontenttype", "json"); parms.Add("body", ""); parms.Add("requesttype", "text/plain"); parms.Add("timeout", "12000"); var markupItems = Regex.Matches(resolvedMarkup, @"(\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) { parms.AddOrReplace(itemParts[0].Trim().ToLower(), itemParts[1].Trim().Substring(1, itemParts[1].Length - 2)); } } return(parms); }
/// <summary> /// Parses the markup. /// </summary> /// <param name="markup">The markup.</param> /// <param name="context">The context.</param> /// <returns></returns> private Dictionary <string, string> ParseMarkup(string markup, ILavaRenderContext context) { // first run lava across the inputted markup var internalMergeFields = context.GetMergeFields(); var resolvedMarkup = markup.ResolveMergeFields(internalMergeFields); var parms = new Dictionary <string, string>(); parms.Add(SCHEDULE_ID, ""); parms.Add(ROLE_ID, ""); parms.Add(SHOW_WHEN, "live"); parms.Add(LOOK_AHEAD_DAYS, "30"); parms.Add(SCHEDULE_CATEGORY_ID, ""); parms.Add(AS_AT_DATE, ""); var markupItems = Regex.Matches(resolvedMarkup, @"(\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) { parms.AddOrReplace(itemParts[0].Trim().ToLower(), itemParts[1].Trim().Substring(1, itemParts[1].Length - 2)); } } return(parms); }
/// <summary> /// Parses the markup. /// </summary> /// <param name="markup">The markup.</param> /// <param name="context">The context.</param> /// <returns></returns> private Dictionary <string, string> ParseMarkup(string markup, ILavaRenderContext context) { // first run lava across the inputted markup var internalMergeFields = context.GetMergeFields(); var resolvedMarkup = markup.ResolveMergeFields(internalMergeFields); return(ParseResolvedMarkup(resolvedMarkup)); }
/// <summary> /// Parses the Lava Command markup, first resolving merge fields and then harvesting any provided parameters. /// </summary> /// <param name="markup">The Lava Command markup.</param> /// <param name="context">The DotLiquid context.</param> /// <param name="parms"> /// A dictionary into which any parameters discovered within the <paramref name="markup"/> will be added or replaced. /// Default values may be pre-loaded into this collection, and will be overwritten if a matching key is present within the <paramref name="markup"/>. /// Note that parameter keys should be added in lower case. /// <para> /// When searching the <paramref name="markup"/> for key/value parameter pairs, the following <see cref="Regex"/> pattern will be used: @"\S+:('[^']+'|\d+)". /// This means that the following patterns will be matched: "key:'value'" OR "key:integer". While this should work for most - if not all - Lava Command parameters, /// you can always choose to not use this helper method and instead roll your own implementation. /// </para> /// </param> public static void ParseCommandMarkup(string markup, ILavaRenderContext context, Dictionary <string, string> parms) { if (markup.IsNull()) { return; } if (context == null) { throw new ArgumentNullException(nameof(context)); } if (parms == null) { throw new ArgumentNullException(nameof(parms)); } var mergeFields = context.GetMergeFields(); // Resolve merge fields. var resolvedMarkup = markup.ResolveMergeFields(mergeFields); // Harvest parameters. var markupParms = Regex.Matches(resolvedMarkup, @"\S+:('[^']+'|\d+)") .Cast <Match>() .Select(m => m.Value) .ToList(); foreach (var parm in markupParms) { var itemParts = parm.ToString().Split(new char[] { ':' }, 2); if (itemParts.Length > 1) { var key = itemParts[0].Trim().ToLower(); var value = itemParts[1].Trim(); if (value[0] == '\'') { // key:'value' parms.AddOrReplace(key, value.Substring(1, value.Length - 2)); } else { // key:integer parms.AddOrReplace(key, value); } } } }
/// <summary> /// Merges the lava. /// </summary> /// <param name="lavaTemplate">The lava template.</param> /// <param name="context">The context.</param> /// <returns></returns> private string MergeLava(string lavaTemplate, ILavaRenderContext context) { // Resolve the Lava template contained in this block in a new context. var engine = context.GetService <ILavaEngine>(); var newContext = engine.NewRenderContext(); newContext.SetMergeFields(context.GetMergeFields()); newContext.SetInternalFields(context.GetInternalFields()); // Resolve the inner template. var result = engine.RenderTemplate(lavaTemplate, LavaRenderParameters.WithContext(newContext)); return(result.Text); }
/// <summary> /// Parses the markup. /// </summary> /// <param name="markup">The markup.</param> /// <param name="context">The context.</param> /// <returns></returns> private Dictionary <string, string> ParseMarkup(string markup, ILavaRenderContext context) { // first run lava across the inputted markup var internalMergeFields = context.GetMergeFields(); /* * var internalMergeFields = new Dictionary<string, object>(); * * // get variables defined in the lava source * foreach ( var scope in context.GetScopes ) * { * foreach ( var item in scope ) * { * internalMergeFields.AddOrReplace( item.Key, item.Value ); * } * } * * // get merge fields loaded by the block or container * if ( context.GetEnvironments.Count > 0 ) * { * foreach ( var item in context.GetEnvironments[0] ) * { * internalMergeFields.AddOrReplace( item.Key, item.Value ); * } * } */ var resolvedMarkup = markup.ResolveMergeFields(internalMergeFields); var parms = new Dictionary <string, string>(); parms.Add("cacheduration", "0"); var markupItems = Regex.Matches(resolvedMarkup, @"(\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) { parms.AddOrReplace(itemParts[0].Trim().ToLower(), itemParts[1].Trim().Substring(1, itemParts[1].Length - 2)); } } return(parms); }
/// <summary> /// Parses the markup. /// </summary> /// <param name="markup">The markup.</param> /// <param name="context">The context.</param> /// <returns></returns> private Dictionary <string, string> ParseMarkup(string markup, ILavaRenderContext context) { // first run lava across the inputted markup var internalMergeFields = context.GetMergeFields(); /* * var internalMergeFields = new Dictionary<string, object>(); * * // get variables defined in the lava source * foreach ( var scope in context.GetScopes ) * { * foreach ( var item in scope ) * { * internalMergeFields.AddOrReplace( item.Key, item.Value ); * } * } * * // get merge fields loaded by the block or container * if ( context.GetEnvironments.Count > 0 ) * { * foreach ( var item in context.GetEnvironments[0] ) * { * internalMergeFields.AddOrReplace( item.Key, item.Value ); * } * } */ var resolvedMarkup = markup.ResolveMergeFields(internalMergeFields); var parms = new Dictionary <string, string>(); parms.Add("iterator", string.Format("{0}Items", EntityName)); parms.Add("securityenabled", "true"); var markupItems = Regex.Matches(resolvedMarkup, @"(\S*?:'[^']+')") .Cast <Match>() .Select(m => m.Value) .ToList(); if (markupItems.Count == 0) { throw new Exception("No parameters were found in your command. The syntax for a parameter is parmName:'' (note that you must use single quotes)."); } foreach (var item in markupItems) { var itemParts = item.ToString().Split(new char[] { ':' }, 2); if (itemParts.Length > 1) { parms.AddOrReplace(itemParts[0].Trim().ToLower(), itemParts[1].Trim().Substring(1, itemParts[1].Length - 2)); } } // override any dynamic parameters List <string> dynamicFilters = new List <string>(); // will be used to process dynamic filters if (parms.ContainsKey("dynamicparameters")) { var dynamicParms = parms["dynamicparameters"]; var dynamicParmList = dynamicParms.Split(',') .Select(x => x.Trim()) .Where(x => !string.IsNullOrWhiteSpace(x)) .ToList(); foreach (var dynamicParm in dynamicParmList) { if (HttpContext.Current.Request[dynamicParm] != null) { var dynamicParmValue = HttpContext.Current.Request[dynamicParm].ToString(); switch (dynamicParm) { case "id": case "limit": case "offset": case "dataview": case "expression": case "sort": case "iterator": case "checksecurity": case "includedeceased": case "lazyloadenabled": case "include": case "select": case "selectmany": case "groupby": case "securityenabled": { parms.AddOrReplace(dynamicParm, dynamicParmValue); break; } default: { dynamicFilters.Add(dynamicParm); break; } } } } parms.AddOrReplace("dynamicparameters", string.Join(",", dynamicFilters)); } return(parms); }
/// <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()); }
/// <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) { var rockContext = LavaHelper.GetRockContextFromLavaContext(context); // Get enabled security commands _enabledSecurityCommands = context.GetEnabledCommands().JoinStrings(","); using (TextWriter writer = new StringWriter()) { bool filterProvided = false; base.OnRender(context, writer); var parms = ParseMarkup(_markup, context); var lookAheadDays = parms[LOOK_AHEAD_DAYS].AsInteger(); var scheduleCategoryId = parms[SCHEDULE_CATEGORY_ID].AsIntegerOrNull(); var asAtDate = parms[AS_AT_DATE].AsDateTime(); var now = asAtDate ?? RockDateTime.Now; var scheduleIds = new List <int>(); var requestedSchedules = parms[SCHEDULE_ID].StringToIntList(); var schedulesQry = new ScheduleService(rockContext).Queryable().AsNoTracking() .Where(s => s.IsActive == true); if (requestedSchedules.Count() > 0) { schedulesQry = schedulesQry.Where(s => requestedSchedules.Contains(s.Id)); filterProvided = true; } if (scheduleCategoryId.HasValue) { schedulesQry = schedulesQry.Where(s => s.CategoryId == scheduleCategoryId.Value); filterProvided = true; } // If neither a schedule id nor a schedule category id was provided stop if (!filterProvided) { return; } // Get the schedules are order them by the next start time var schedules = schedulesQry.ToList() .Where(s => s.GetNextStartDateTime(now) != null) .OrderBy(s => s.GetNextStartDateTime(now)); if (schedules.Count() == 0) { return; } var nextSchedule = schedules.FirstOrDefault(); var nextStartDateTime = nextSchedule.GetNextStartDateTime(now); var isLive = false; DateTime?occurrenceEndDateTime = null; // Determine if we're live if (nextSchedule.WasScheduleActive(now)) { isLive = true; var occurrences = nextSchedule.GetICalOccurrences(now, now.AddDays(lookAheadDays)).Take(2); var activeOccurrence = occurrences.FirstOrDefault(); occurrenceEndDateTime = (DateTime)activeOccurrence.Period.EndTime.Value; // Set the next occurrence to be the literal next occurrence (vs the current occurrence) nextStartDateTime = null; if (occurrences.Count() > 1) { nextStartDateTime = occurrences.Last().Period.EndTime.Value; } } // Determine when not to show the content if ((parms[SHOW_WHEN] == "notlive" && isLive) || (parms[SHOW_WHEN] == "live" && !isLive)) { return; } // Check role membership var roleId = parms[ROLE_ID].AsIntegerOrNull(); if (roleId.HasValue) { var currentPerson = GetCurrentPerson(context); var isInRole = new GroupMemberService(rockContext).Queryable() .Where(m => m.GroupId == roleId && m.PersonId == currentPerson.Id ) .Any(); if (!isInRole) { return; } } var mergeFields = context.GetMergeFields(); mergeFields.Add("NextOccurrenceDateTime", nextStartDateTime); mergeFields.Add("OccurrenceEndDateTime", occurrenceEndDateTime); mergeFields.Add("Schedule", nextSchedule); mergeFields.Add("IsLive", isLive); var engine = context.GetService <ILavaEngine>(); var renderContext = engine.NewRenderContext(mergeFields, _enabledSecurityCommands.SplitDelimitedValues()); var results = engine.RenderTemplate(_blockMarkup.ToString(), LavaRenderParameters.WithContext(renderContext)); result.Write(results.Text.Trim()); } }