public override async Task WriteAsync(TemplateScopeContext scope, PageBlockFragment fragment, CancellationToken cancel)
        {
            var strFragment = (PageStringFragment)fragment.Body[0];

            if (!fragment.Argument.IsNullOrWhiteSpace())
            {
                var literal = fragment.Argument.AdvancePastWhitespace();
                bool appendTo = false;
                if (literal.StartsWith("appendTo "))
                {
                    appendTo = true;
                    literal = literal.Advance("appendTo ".Length);
                }
                
                literal = literal.ParseVarName(out var name);
                var nameString = name.Value;
                if (appendTo && scope.PageResult.Args.TryGetValue(nameString, out var oVar)
                    && oVar is string existingString)
                {
                    scope.PageResult.Args[nameString] = existingString + strFragment.Value.Value;
                    return;
                }
                
                scope.PageResult.Args[nameString] = strFragment.Value.Value; 
            }
            else
            {
                await scope.OutputStream.WriteAsync(strFragment.Value, cancel);
            }
        }
        public override async Task WriteAsync(TemplateScopeContext scope, PageBlockFragment block, CancellationToken token)
        {
            var argValue = block.Argument.GetJsExpressionAndEvaluate(scope);
            var args     = argValue as Dictionary <string, object> ?? new Dictionary <string, object>();

            var format = scope.Context.PageFormats.First().Extension;

            if (args.TryGetValue("format", out var oFormat))
            {
                format = oFormat.ToString();
                args.Remove("format");
            }

            var unrenderedBody = new TemplatePartialPage(scope.Context, "evalSafe-page", block.Body, format, args);

            using (var ms = MemoryStreamFactory.GetStream())
            {
                var captureScope = scope.ScopeWith(outputStream: ms, scopedParams: args);
                await scope.PageResult.WritePageAsync(unrenderedBody, captureScope, token);

                var renderedBody = await ms.ReadToEndAsync();

                var context    = new TemplateContext().Init();
                var pageResult = new PageResult(context.OneTimePage(renderedBody))
                {
                    Args = args,
                };
                await pageResult.WriteToAsync(scope.OutputStream, token);
            }
        }
Exemple #3
0
        public override Task WriteAsync(TemplateScopeContext scope, PageBlockFragment fragment, CancellationToken cancel)
        {
            var literal = fragment.Argument.ParseVarName(out var name);

            if (name.IsNullOrEmpty())
            {
                throw new NotSupportedException("'partial' block is missing name of partial");
            }

            literal = literal.AdvancePastWhitespace();

            var argValue = literal.GetJsExpressionAndEvaluate(scope);
            var args     = argValue as Dictionary <string, object>;

            if (argValue != null && args == null)
            {
                throw new NotSupportedException("Any 'partial' argument must be an Object Dictionary");
            }

            var format = scope.Context.PageFormats.First().Extension;

            if (args != null && args.TryGetValue("format", out var oFormat))
            {
                format = oFormat.ToString();
                args.Remove("format");
            }

            var nameString = name.Value;
            var partial    = new TemplatePartialPage(scope.Context, nameString, fragment.Body, format, args);

            scope.PageResult.Partials[nameString] = partial;

            return(TypeConstants.EmptyTask);
        }
Exemple #4
0
        public override async Task WriteAsync(TemplateScopeContext scope, PageBlockFragment fragment, CancellationToken cancel)
        {
            if (fragment.Argument.IsNullOrWhiteSpace())
            {
                throw new NotSupportedException("'capture' block is missing name of variable to assign captured output to");
            }

            var  literal  = fragment.Argument.AdvancePastWhitespace();
            bool appendTo = false;

            if (literal.StartsWith("appendTo "))
            {
                appendTo = true;
                literal  = literal.Advance("appendTo ".Length);
            }

            literal = literal.ParseVarName(out var name);
            if (name.IsNullOrEmpty())
            {
                throw new NotSupportedException("'capture' block is missing name of variable to assign captured output to");
            }

            literal = literal.AdvancePastWhitespace();

            var argValue = literal.GetJsExpressionAndEvaluate(scope);

            var scopeArgs = argValue as Dictionary <string, object>;

            if (argValue != null && scopeArgs == null)
            {
                throw new NotSupportedException("Any 'capture' argument must be an Object Dictionary");
            }


            var ms       = MemoryStreamFactory.GetStream();
            var useScope = scope.ScopeWith(scopeArgs, ms);

            await WriteBodyAsync(useScope, fragment, cancel);

            var capturedOutput = ms.ReadToEnd();

            var nameString = name.Value;

            if (appendTo && scope.PageResult.Args.TryGetValue(nameString, out var oVar) &&
                oVar is string existingString)
            {
                scope.PageResult.Args[nameString] = existingString + capturedOutput;
                return;
            }

            scope.PageResult.Args[nameString] = capturedOutput;
        }
Exemple #5
0
        public override async Task WriteAsync(TemplateScopeContext scope, PageBlockFragment block, CancellationToken token)
        {
            var strFragment = (PageStringFragment)block.Body[0];

            if (!block.Argument.IsNullOrWhiteSpace())
            {
                Capture(scope, block, strFragment);
            }
            else
            {
                await scope.OutputStream.WriteAsync(strFragment.Value.Span, token);
            }
        }
Exemple #6
0
        public override async Task WriteAsync(TemplateScopeContext scope, PageBlockFragment block, CancellationToken token)
        {
            var result = await block.Argument.GetJsExpressionAndEvaluateToBoolAsync(scope,
                                                                                    ifNone : () => throw new NotSupportedException("'if' block does not have a valid expression"));

            if (result)
            {
                await WriteBodyAsync(scope, block, token);
            }
            else
            {
                await WriteElseAsync(scope, block.ElseBlocks, token);
            }
        }
        public override async Task WriteAsync(TemplateScopeContext scope, PageBlockFragment fragment, CancellationToken cancel)
        {
            if (string.IsNullOrEmpty(fragment.ArgumentString))
            {
                throw new NotSupportedException("'each' block requires the collection to iterate");
            }

            var cache = (EachArg)scope.Context.Cache.GetOrAdd(fragment.ArgumentString, _ => ParseArgument(scope, fragment));

            IEnumerable collection = (IEnumerable)await cache.Source.EvaluateAsync(scope);

            var index      = 0;
            var whereIndex = 0;

            if (collection != null)
            {
                foreach (var element in collection)
                {
                    // Add all properties into scope if called without explicit in argument
                    var scopeArgs = !cache.HasExplicitBinding && CanExportScopeArgs(element)
                        ? element.ToObjectDictionary()
                        : new Dictionary <string, object>();

                    scopeArgs[cache.Binding] = element;
                    scopeArgs[nameof(index)] = AssertWithinMaxQuota(whereIndex++);
                    var itemScope = scope.ScopeWithParams(scopeArgs);

                    if (cache.Where != null)
                    {
                        var result = await cache.Where.EvaluateToBoolAsync(itemScope);

                        if (!result)
                        {
                            continue;
                        }
                    }

                    itemScope.ScopedParams[nameof(index)] = AssertWithinMaxQuota(index++);

                    await WriteBodyAsync(itemScope, fragment, cancel);
                }
            }

            if (index == 0)
            {
                await WriteElseBlocks(scope, fragment.ElseBlocks, cancel);
            }
        }
        EachArg ParseArgument(TemplateScopeContext scope, PageBlockFragment fragment)
        {
            var literal = fragment.Argument.ParseJsExpression(out var token);

            if (token == null)
            {
                throw new NotSupportedException("'each' block requires the collection to iterate");
            }

            var binding = "it";

            literal = literal.AdvancePastWhitespace();

            JsToken source, where = null;

            var hasExplicitBinding = literal.StartsWith("in ");

            if (hasExplicitBinding)
            {
                if (!(token is JsIdentifier identifier))
                {
                    throw new NotSupportedException($"'each' block expected identifier but was {token.DebugToken()}");
                }

                binding = identifier.NameString;

                literal = literal.Advance(3);
                literal = literal.ParseJsExpression(out source);
                if (source == null)
                {
                    throw new NotSupportedException("'each' block requires the collection to iterate");
                }
            }
            else
            {
                source = token;
            }

            if (literal.StartsWith("where "))
            {
                literal = literal.Advance(6);
                literal = literal.ParseJsExpression(out where);
            }

            return(new EachArg(binding, hasExplicitBinding, source, where));
        }
Exemple #9
0
        public override async Task WriteAsync(TemplateScopeContext scope, PageBlockFragment fragment, CancellationToken cancel)
        {
            var result = await fragment.Argument.GetJsExpressionAndEvaluateAsync(scope,
                                                                                 ifNone : () => throw new NotSupportedException("'with' block does not have a valid expression"));

            if (result != null)
            {
                var resultAsMap = result.ToObjectDictionary();

                var withScope = scope.ScopeWithParams(resultAsMap);

                await WriteBodyAsync(withScope, fragment, cancel);
            }
            else
            {
                await WriteElseBlocks(scope, fragment.ElseBlocks, cancel);
            }
        }
        public override async Task WriteAsync(TemplateScopeContext scope, PageBlockFragment fragment, CancellationToken cancel)
        {
            var(name, scopeArgs, appendTo) = Parse(scope, fragment);

            var ms       = MemoryStreamFactory.GetStream();
            var useScope = scope.ScopeWith(scopeArgs, ms);

            await WriteBodyAsync(useScope, fragment, cancel);

            var capturedOutput = ms.ReadToEnd();

            if (appendTo && scope.PageResult.Args.TryGetValue(name, out var oVar) &&
                oVar is string existingString)
            {
                scope.PageResult.Args[name] = existingString + capturedOutput;
                return;
            }

            scope.PageResult.Args[name] = capturedOutput;
        }
Exemple #11
0
        private static void Capture(TemplateScopeContext scope, PageBlockFragment block, PageStringFragment strFragment)
        {
            var  literal  = block.Argument.Span.AdvancePastWhitespace();
            bool appendTo = false;

            if (literal.StartsWith("appendTo "))
            {
                appendTo = true;
                literal  = literal.Advance("appendTo ".Length);
            }

            literal = literal.ParseVarName(out var name);
            var nameString = name.Value();

            if (appendTo && scope.PageResult.Args.TryGetValue(nameString, out var oVar) &&
                oVar is string existingString)
            {
                scope.PageResult.Args[nameString] = existingString + strFragment.Value;
                return;
            }

            scope.PageResult.Args[nameString] = strFragment.Value.ToString();
        }
        public override async Task WriteAsync(TemplateScopeContext scope, PageBlockFragment block, CancellationToken token)
        {
            var argValue = block.Argument.GetJsExpressionAndEvaluate(scope);
            var args     = argValue as Dictionary <string, object> ?? new Dictionary <string, object>();

            var format = scope.Context.PageFormats.First().Extension;

            if (args.TryGetValue(TemplateConstants.Format, out var oFormat))
            {
                format = oFormat.ToString();
                args.Remove(TemplateConstants.Format);
            }

            var htmlDecode = false;

            if (args.TryGetValue(nameof(htmlDecode), out var oHtmlDecode) &&
                oHtmlDecode is bool b)
            {
                htmlDecode = b;
                args.Remove(nameof(htmlDecode));
            }

            var context = new TemplateContext();

            if (args.TryGetValue("use", out var oUse))
            {
                var use = (Dictionary <string, object>)oUse;
                if (use.TryGetValue("context", out var oContext) && oContext is bool useContext && useContext)
                {
                    context = scope.Context;
                }
                else
                {
                    // Use same ThreadSafe plugin instance to preserve configuration
                    var plugins = use.TryGetValue("plugins", out var oPlugins)
                        ? ToStrings("plugins", oPlugins)
                        : null;
                    if (plugins != null)
                    {
                        foreach (var name in plugins)
                        {
                            var plugin = scope.Context.Plugins.FirstOrDefault(x => x.GetType().Name == name);
                            if (plugin == null)
                            {
                                throw new NotSupportedException($"Plugin '{name}' is not registered in parent context");
                            }

                            context.Plugins.Add(plugin);
                        }
                    }

                    // Use new filter and block instances which cannot be shared between contexts
                    var filters = use.TryGetValue("filters", out var oFilters)
                        ? ToStrings("filters", oFilters)
                        : null;
                    if (filters != null)
                    {
                        foreach (var name in filters)
                        {
                            var filter = scope.Context.TemplateFilters.FirstOrDefault(x => x.GetType().Name == name);
                            if (filter == null)
                            {
                                throw new NotSupportedException($"Filter '{name}' is not registered in parent context");
                            }

                            context.TemplateFilters.Add(filter.GetType().CreateInstance <TemplateFilter>());
                        }
                    }

                    var blocks = use.TryGetValue("blocks", out var oBlocks)
                        ? ToStrings("blocks", oBlocks)
                        : null;
                    if (blocks != null)
                    {
                        foreach (var name in blocks)
                        {
                            var useBlock = scope.Context.TemplateBlocks.FirstOrDefault(x => x.GetType().Name == name);
                            if (useBlock == null)
                            {
                                throw new NotSupportedException($"Block '{name}' is not registered in parent context");
                            }

                            context.TemplateBlocks.Add(useBlock.GetType().CreateInstance <TemplateBlock>());
                        }
                    }
                }

                args.Remove(nameof(use));
            }
Exemple #13
0
 protected virtual async Task WriteBodyAsync(TemplateScopeContext scope, PageBlockFragment fragment, CancellationToken token)
 {
     await WriteAsync(scope, fragment.Body, GetCallTrace(fragment), token);
 }
Exemple #14
0
 public abstract Task WriteAsync(TemplateScopeContext scope, PageBlockFragment block, CancellationToken token);
Exemple #15
0
 protected virtual string GetCallTrace(PageBlockFragment fragment) => "Block: " + Name +
 (fragment.Argument.IsNullOrEmpty() ? "" : " (" + fragment.Argument + ")");
Exemple #16
0
 public override Task WriteAsync(TemplateScopeContext scope, PageBlockFragment fragment, CancellationToken cancel) =>
 TypeConstants.EmptyTask;
Exemple #17
0
        public override async Task WriteAsync(TemplateScopeContext scope, PageBlockFragment fragment, CancellationToken cancel)
        {
            var         htmlAttrs          = fragment.Argument.GetJsExpressionAndEvaluate(scope) as Dictionary <string, object>;
            var         hasEach            = false;
            IEnumerable each               = null;
            var         binding            = "it";
            var         hasExplicitBinding = false;

            JsToken where = null;

            if (htmlAttrs != null)
            {
                if (htmlAttrs.TryGetValue("if", out var oIf))
                {
                    if (TemplateDefaultFilters.isFalsy(oIf))
                    {
                        return;
                    }
                    htmlAttrs.Remove("if");
                }

                if (htmlAttrs.TryGetValue(nameof(where), out var oWhere))
                {
                    if (!(oWhere is string whereExpr))
                    {
                        throw new NotSupportedException($"'where' should be a string expression but instead found '{oWhere.GetType().Name}'");
                    }

                    where = whereExpr.GetCachedJsExpression(scope);
                    htmlAttrs.Remove(nameof(where));
                }

                if (htmlAttrs.TryGetValue(nameof(each), out var oEach))
                {
                    hasEach = true;
                    htmlAttrs.Remove(nameof(each));
                }
                each = oEach as IEnumerable;

                if (htmlAttrs.TryGetValue("it", out var oIt) && oIt is string it)
                {
                    binding            = it;
                    hasExplicitBinding = true;
                    htmlAttrs.Remove("it");
                }

                if (htmlAttrs.TryGetValue("class", out var oClass))
                {
                    var cls = scope.Context.HtmlFilters.htmlClassList(oClass);
                    if (string.IsNullOrEmpty(cls))
                    {
                        htmlAttrs.Remove("class");
                    }
                    else
                    {
                        htmlAttrs["class"] = cls;
                    }
                }
            }

            var attrString = scope.Context.HtmlFilters.htmlAttrsList(htmlAttrs);

            if (TemplateHtmlFilters.VoidElements.Contains(Tag)) //e.g. img, input, br, etc
            {
                await scope.OutputStream.WriteAsync($"<{Tag}{attrString}>{Suffix}", cancel);
            }
            else
            {
                if (hasEach)
                {
                    var hasElements = each != null && each.GetEnumerator().MoveNext();
                    if (hasElements)
                    {
                        await scope.OutputStream.WriteAsync($"<{Tag}{attrString}>{Suffix}", cancel);

                        var index      = 0;
                        var whereIndex = 0;
                        foreach (var element in each)
                        {
                            // Add all properties into scope if called without explicit in argument
                            var scopeArgs = !hasExplicitBinding && CanExportScopeArgs(element)
                                ? element.ToObjectDictionary()
                                : new Dictionary <string, object>();

                            scopeArgs[binding]       = element;
                            scopeArgs[nameof(index)] = AssertWithinMaxQuota(whereIndex++);
                            var itemScope = scope.ScopeWithParams(scopeArgs);

                            if (where != null)
                            {
                                var result = where.EvaluateToBool(itemScope);
                                if (!result)
                                {
                                    continue;
                                }
                            }

                            itemScope.ScopedParams[nameof(index)] = AssertWithinMaxQuota(index++);

                            await WriteBodyAsync(itemScope, fragment, cancel);
                        }

                        await scope.OutputStream.WriteAsync($"</{Tag}>{Suffix}", cancel);
                    }
                    else
                    {
                        await WriteElseBlocks(scope, fragment.ElseBlocks, cancel);
                    }
                }
                else
                {
                    await scope.OutputStream.WriteAsync($"<{Tag}{attrString}>{Suffix}", cancel);
                    await WriteBodyAsync(scope, fragment, cancel);

                    await scope.OutputStream.WriteAsync($"</{Tag}>{Suffix}", cancel);
                }
            }
        }
Exemple #18
0
        public override async Task WriteAsync(TemplateScopeContext scope, PageBlockFragment fragment, CancellationToken cancel)
        {
            if (string.IsNullOrEmpty(fragment.ArgumentString))
            {
                throw new NotSupportedException("'each' block requires the collection to iterate");
            }

            var cache = (EachArg)scope.Context.Cache.GetOrAdd(fragment.ArgumentString, _ => ParseArgument(scope, fragment));

            var collection = cache.Source.Evaluate(scope, out var syncResult, out var asyncResult)
                ? (IEnumerable)syncResult
                : (IEnumerable)(await asyncResult);

            var index = 0;

            if (collection != null)
            {
                if (cache.Where != null || cache.OrderBy != null || cache.OrderByDescending != null ||
                    cache.Skip != null || cache.Take != null)
                {
                    var filteredResults = new List <Dictionary <string, object> >();
                    foreach (var element in collection)
                    {
                        // Add all properties into scope if called without explicit in argument
                        var scopeArgs = !cache.HasExplicitBinding && CanExportScopeArgs(element)
                            ? element.ToObjectDictionary()
                            : new Dictionary <string, object>();

                        scopeArgs[cache.Binding] = element;
                        scopeArgs[nameof(index)] = AssertWithinMaxQuota(index++);
                        var itemScope = scope.ScopeWithParams(scopeArgs);

                        if (cache.Where != null)
                        {
                            var result = await cache.Where.EvaluateToBoolAsync(itemScope);

                            if (!result)
                            {
                                continue;
                            }
                        }

                        filteredResults.Add(scopeArgs);
                    }

                    IEnumerable <Dictionary <string, object> > selectedResults = filteredResults;

                    var comparer = (IComparer <object>)Comparer <object> .Default;
                    if (cache.OrderBy != null)
                    {
                        var i = 0;
                        selectedResults = selectedResults.OrderBy(scopeArgs =>
                        {
                            scopeArgs[nameof(index)] = i++;
                            return(cache.OrderBy.Evaluate(scope.ScopeWithParams(scopeArgs)));
                        }, comparer);
                    }
                    else if (cache.OrderByDescending != null)
                    {
                        var i = 0;
                        selectedResults = selectedResults.OrderByDescending(scopeArgs =>
                        {
                            scopeArgs[nameof(index)] = i++;
                            return(cache.OrderByDescending.Evaluate(scope.ScopeWithParams(scopeArgs)));
                        }, comparer);
                    }

                    if (cache.Skip != null)
                    {
                        var skip = cache.Skip.Evaluate(scope).ConvertTo <int>();
                        selectedResults = selectedResults.Skip(skip);
                    }

                    if (cache.Take != null)
                    {
                        var take = cache.Take.Evaluate(scope).ConvertTo <int>();
                        selectedResults = selectedResults.Take(take);
                    }

                    index = 0;
                    foreach (var scopeArgs in selectedResults)
                    {
                        var itemScope = scope.ScopeWithParams(scopeArgs);
                        itemScope.ScopedParams[nameof(index)] = index++;
                        await WriteBodyAsync(itemScope, fragment, cancel);
                    }
                }
                else
                {
                    foreach (var element in collection)
                    {
                        // Add all properties into scope if called without explicit in argument
                        var scopeArgs = !cache.HasExplicitBinding && CanExportScopeArgs(element)
                            ? element.ToObjectDictionary()
                            : new Dictionary <string, object>();

                        scopeArgs[cache.Binding] = element;
                        scopeArgs[nameof(index)] = AssertWithinMaxQuota(index++);
                        var itemScope = scope.ScopeWithParams(scopeArgs);

                        await WriteBodyAsync(itemScope, fragment, cancel);
                    }
                }
            }

            if (index == 0)
            {
                await WriteElseBlocks(scope, fragment.ElseBlocks, cancel);
            }
        }
Exemple #19
0
        EachArg ParseArgument(TemplateScopeContext scope, PageBlockFragment fragment)
        {
            var literal = fragment.Argument.Span.ParseJsExpression(out var token);

            if (token == null)
            {
                throw new NotSupportedException("'each' block requires the collection to iterate");
            }

            var binding = "it";

            literal = literal.AdvancePastWhitespace();

            JsToken source, where, orderBy, orderByDescending, skip, take;

            where = orderBy = orderByDescending = skip = take = null;

            var hasExplicitBinding = literal.StartsWith("in ");

            if (hasExplicitBinding)
            {
                if (!(token is JsIdentifier identifier))
                {
                    throw new NotSupportedException($"'each' block expected identifier but was {token.DebugToken()}");
                }

                binding = identifier.Name;

                literal = literal.Advance(3);
                literal = literal.ParseJsExpression(out source);
                if (source == null)
                {
                    throw new NotSupportedException("'each' block requires the collection to iterate");
                }
            }
            else
            {
                source = token;
            }

            if (literal.StartsWith("where "))
            {
                literal = literal.Advance("where ".Length);
                literal = literal.ParseJsExpression(out where);
            }

            literal = literal.AdvancePastWhitespace();
            if (literal.StartsWith("orderby "))
            {
                literal = literal.Advance("orderby ".Length);
                literal = literal.ParseJsExpression(out orderBy);

                literal = literal.AdvancePastWhitespace();
                if (literal.StartsWith("descending"))
                {
                    literal           = literal.Advance("descending".Length);
                    orderByDescending = orderBy;
                    orderBy           = null;
                }
            }

            literal = literal.AdvancePastWhitespace();
            if (literal.StartsWith("skip "))
            {
                literal = literal.Advance("skip ".Length);
                literal = literal.ParseJsExpression(out skip);
            }

            literal = literal.AdvancePastWhitespace();
            if (literal.StartsWith("take "))
            {
                literal = literal.Advance("take ".Length);
                literal = literal.ParseJsExpression(out take);
            }

            return(new EachArg(binding, hasExplicitBinding, source, where, orderBy, orderByDescending, skip, take));
        }
        //Extract Span out of async method
        private (string name, Dictionary <string, object> scopeArgs, bool appendTo) Parse(TemplateScopeContext scope, PageBlockFragment fragment)
        {
            if (fragment.Argument.IsNullOrWhiteSpace())
            {
                throw new NotSupportedException("'capture' block is missing name of variable to assign captured output to");
            }

            var  literal  = fragment.Argument.AdvancePastWhitespace();
            bool appendTo = false;

            if (literal.StartsWith("appendTo "))
            {
                appendTo = true;
                literal  = literal.Advance("appendTo ".Length);
            }

            literal = literal.ParseVarName(out var name);
            if (name.IsNullOrEmpty())
            {
                throw new NotSupportedException("'capture' block is missing name of variable to assign captured output to");
            }

            literal = literal.AdvancePastWhitespace();

            var argValue = literal.GetJsExpressionAndEvaluate(scope);

            var scopeArgs = argValue as Dictionary <string, object>;

            if (argValue != null && scopeArgs == null)
            {
                throw new NotSupportedException("Any 'capture' argument must be an Object Dictionary");
            }

            return(name.ToString(), scopeArgs, appendTo);
        }
Exemple #21
0
 public override Task WriteAsync(ScriptScopeContext scope, PageBlockFragment block, CancellationToken token) =>
 WriteAsync((TemplateScopeContext)scope, block, token);