예제 #1
0
        public static FluidValue Truncate(FluidValue input, FilterArguments arguments, TemplateContext context)
        {
            if (input.IsNil())
            {
                return(StringValue.Empty);
            }

            var inputStr = input.ToStringValue();

            if (inputStr == null)
            {
                return(StringValue.Empty);
            }

            var ellipsisStr = arguments.At(1).Or(Ellipsis).ToStringValue();

            var length = Convert.ToInt32(arguments.At(0).Or(NumberValue.Create(50)).ToNumberValue());

            var l = Math.Max(0, length - ellipsisStr.Length);

            return(inputStr.Length > length
                ? new StringValue(inputStr.Substring(0, l) + ellipsisStr)
                : input
                   );
        }
예제 #2
0
        public override ValueTask <Completion> WriteToAsync(TextWriter writer, TextEncoder encoder, TemplateContext context)
        {
            // We prefix the identifier to prevent collisions with variables.
            // Variable identifiers don't represent the same slots as inc/dec ones.
            // c.f. https://shopify.github.io/liquid/tags/variable/

            var prefixedIdentifier = Prefix + Identifier;

            var value = context.GetValue(prefixedIdentifier);

            if (value.IsNil())
            {
                value = NumberValue.Zero;
            }
            else
            {
                value = NumberValue.Create(value.ToNumberValue() + 1);
            }

            context.SetValue(prefixedIdentifier, value);

            value.WriteTo(writer, encoder, context.CultureInfo);

            return(Normal);
        }
예제 #3
0
 // Step to next token, taking action as we go
 internal void NextToken()
 {
     while (_tokenindex < _tokenlist.Count - 1)
     {
         ++_tokenindex;
         var token = _tokenlist[_tokenindex];
         if (token.TokenType == TokenTypes.LINE)
         {
             _symbols.Find("$lineno$").Value = NumberValue.Create(token.LineNumber);
             if (Logger.Level > 1 || (Logger.Level == 1 && token.SourcePath == _tokenlist[0].SourcePath))
             {
                 Logger.WriteLine("{0,3}: {1}", token.LineNumber, token.Value);
             }
         }
         else if (token.TokenType == TokenTypes.Directive)
         {
             Directive(token);
         }
         else if (token.TokenType == TokenTypes.Bad)
         {
             ErrLexer(token.LineNumber, "bad token '{0}'", token.Value);
         }
         else
         {
             break;
         }
     }
     Logger.Assert(!_tokenlist[_tokenindex].IsWhite);
     //Logger.WriteLine(6, "Token=<{0}> <{1}>", _tokenlist[_tokenindex], CurrentSymbol);
 }
예제 #4
0
 // Step to next token, taking action as we go
 void MoveNext()
 {
     if (_stop)
     {
         _tokenindex = _tokenlist.Count - 1;
     }
     while (_tokenindex < _tokenlist.Count - 1)
     {
         ++_tokenindex;
         var token = _tokenlist[_tokenindex];
         if (token.TokenType == TokenTypes.LINE)
         {
             _symbols.Find("$lineno$").Value = NumberValue.Create(token.LineNumber);
             if (Logger.Level > 0)
             {
                 Logger.WriteLine("{0,3}: {1}", token.LineNumber, token.Value);
             }
         }
         else if (token.TokenType == TokenTypes.Directive)
         {
             Directive(token);
         }
         else if (token.TokenType == TokenTypes.Bad)
         {
             ErrLexer(token.LineNumber, "bad token '{0}'", token.Value);
         }
         else
         {
             break;
         }
     }
     Logger.Assert(!_tokenlist[_tokenindex].IsWhite);
     Logger.WriteLine(6, "Token=<{0}> <{1}>", _tokenlist[_tokenindex], CurrentSymbol);
 }
예제 #5
0
        public Task Handle(EvaluatingLiquidExpression notification, CancellationToken cancellationToken)
        {
            var context = notification.TemplateContext;
            var options = context.Options;

            options.Scope.SetValue("Request", new ObjectValue(new LiquidRequestAccessor()));

            options.MemberAccessStrategy.Register <LiquidRequestAccessor, FluidValue>((_, name, _) =>
            {
                var request = _httpContextAccessor.HttpContext?.Request;
                if (request == null)
                {
                    return(NilValue.Instance);
                }
                return(name switch
                {
                    nameof(HttpRequest.QueryString) => new StringValue(request.QueryString.Value),
                    nameof(HttpRequest.ContentType) => new StringValue(request.ContentType),
                    nameof(HttpRequest.ContentLength) => NumberValue.Create(request.ContentLength ?? 0),
                    nameof(HttpRequest.Form) => request.HasFormContentType ? new ObjectValue(request.Form) : NilValue.Instance,
                    nameof(HttpRequest.Protocol) => new StringValue(request.Protocol),
                    nameof(HttpRequest.Path) => new StringValue(request.Path.Value),
                    nameof(HttpRequest.PathBase) => new StringValue(request.PathBase.Value),
                    nameof(HttpRequest.Host) => new StringValue(request.Host.Value),
                    nameof(HttpRequest.IsHttps) => BooleanValue.Create(request.IsHttps),
                    nameof(HttpRequest.Scheme) => new StringValue(request.Scheme),
                    nameof(HttpRequest.Method) => new StringValue(request.Method),
                    _ => NilValue.Instance
                });
            });
예제 #6
0
        public async Task ShouldEvaluateFilters(string source, string expected)
        {
            FluidTemplate.TryParse(source, out var template, out var messages);
            var context = new TemplateContext();

            context.Filters.AddFilter("inc", (i, args, ctx) =>
            {
                var increment = 1;
                if (args.Count > 0)
                {
                    increment = (int)args.At(0).ToNumberValue();
                }

                return(NumberValue.Create(i.ToNumberValue() + increment));
            });

            context.Filters.AddFilter("append", (i, args, ctx) =>
            {
                var s = i.ToStringValue();

                for (var k = 0; k < args.Count; k++)
                {
                    s += args.At(k).ToStringValue();
                }

                return(new StringValue(s));
            });

            var result = await template.RenderAsync(context);

            Assert.Equal(expected, result);
        }
예제 #7
0
        public async Task ShouldUseCurrentContext()
        {
            var e = new ForStatement(
                new List <Statement> {
                new AssignStatement("z", new LiteralExpression(NumberValue.Create(1)))
            },
                "i",
                new RangeExpression(
                    new LiteralExpression(NumberValue.Create(1)),
                    new LiteralExpression(NumberValue.Create(3))
                    ),
                null, null, false
                );

            var t = new TemplateContext();

            t.SetValue("z", 0);

            Assert.Equal(0, t.GetValue("z").ToNumberValue());

            var sw = new StringWriter();
            await e.WriteToAsync(sw, HtmlEncoder.Default, t);

            Assert.Equal(1, t.GetValue("z").ToNumberValue());
        }
예제 #8
0
        public void NumbersEquality()
        {
            var num1 = NumberValue.Create(12.5);
            var num2 = NumberValue.Create(12.5);
            var num3 = NumberValue.Create(7);
            var num4 = NumberValue.Create(0);

            Assert.Equal(num1, num2);
            Assert.True(num1.Equals(num2));
            Assert.NotEqual(num1, num3);

            Assert.Equal(0, num4.AsNumber());
            Assert.Equal(7, num3.AsNumber());

            Assert.True(num1.AsBoolean());
            Assert.True(num2.AsBoolean());
            Assert.True(num3.AsBoolean());
            Assert.False(num4.AsBoolean());

            Assert.True(num1.CompareTo(num2) == 0);
            Assert.True(num1.CompareTo(num3) > 0);
            Assert.True(num4.CompareTo(num3) < 0);

            Assert.Equal("12.5", num1.AsString());
            Assert.Throws <RuntimeException>(() => num1.AsDate());
            Assert.Throws <RuntimeException>(() => num1.AsObject());
        }
예제 #9
0
        // Output a call according to the symbol type
        public void OutCall(Symbol symbol, int varargs = 0, CallInfo callinfo = null)
        {
            var ci = callinfo ?? symbol.CallInfo;

            switch (symbol.CallKind)
            {
            case CallKinds.FUNC:
                Logger.Assert(varargs == 0);
                OutCall(Opcodes.CALL, ci.Name, ci.NumArgs, 0);
                break;

            case CallKinds.VFUNC:
                OutCall(Opcodes.CALLV, ci.Name, ci.NumArgs, varargs);
                break;

            case CallKinds.VFUNCT:
                OutCall(Opcodes.CALLVT, ci.Name, ci.NumArgs, varargs);
                break;

            case CallKinds.JFUNC:
                OutLoad(NumberValue.Create((int)symbol.JoinOp));
                OutCall(Opcodes.CALL, ci.Name, ci.NumArgs, varargs);
                break;

            case CallKinds.LFUNC:
                Out(Opcodes.LDLOOKUP);
                OutCall(Opcodes.CALL, ci.Name, ci.NumArgs, varargs);
                break;

            default:
                throw Logger.Fatal(symbol.CallKind);
            }
        }
예제 #10
0
        public async Task ForEvaluatesMemberOptionsOffsetOnly()
        {
            var context = new TemplateContext()
                          .SetValue("offset", 3)
            ;

            var e = new ForStatement(
                new List <Statement> {
                CreateMemberStatement("i")
            },
                "i",
                new RangeExpression(
                    new LiteralExpression(NumberValue.Create(1)),
                    new LiteralExpression(NumberValue.Create(5))
                    ),
                null,
                new MemberExpression(new IdentifierSegment("offset")),
                true
                );

            var sw = new StringWriter();
            await e.WriteToAsync(sw, HtmlEncoder.Default, context);

            Assert.Equal("54", sw.ToString());
        }
예제 #11
0
        public void Number_ToString_IsStringRepresentationOfNumber()
        {
            var value  = NumberValue.Create(-13.536M) as IValue;
            var result = value.ImplicitConversionToStringValue();

            Assert.AreEqual("-13.536", result.Value);
        }
예제 #12
0
        public async Task NegativeLimitShouldStripFromEnd()
        {
            var context = new TemplateContext()
                          .SetValue("limit", -3)
            ;

            var e = new ForStatement(
                new List <Statement> {
                CreateMemberStatement("i")
            },
                "i",
                new RangeExpression(
                    new LiteralExpression(NumberValue.Create(1)),
                    new LiteralExpression(NumberValue.Create(9))
                    ),
                new MemberExpression(new IdentifierSegment("limit")),
                offset: null,
                false
                );

            var sw = new StringWriter();
            await e.WriteToAsync(sw, HtmlEncoder.Default, context);

            Assert.Equal("123456", sw.ToString());
        }
예제 #13
0
        public void Number_ToNumber_IsSameValue()
        {
            var value  = NumberValue.Create(13) as IValue;
            var result = value.ImplicitConversionToNumberValue();

            Assert.AreEqual(13, result.Value);
        }
예제 #14
0
        public void NumberNonZero_ToBool_IsTrue()
        {
            var value  = NumberValue.Create(-0.1M) as IValue;
            var result = value.ImplicitConversionToBoolValue();

            Assert.AreEqual(true, result.Value);
        }
예제 #15
0
        public void NumberZero_ToBool_IsFalse()
        {
            var value  = NumberValue.Create(0) as IValue;
            var result = value.ImplicitConversionToBoolValue();

            Assert.AreEqual(false, result.Value);
        }
예제 #16
0
        /// <summary>
        /// Redefines the True/False keywords to be case-insensitive.
        /// </summary>
        private void DefineLavaTrueFalseAsCaseInsensitive()
        {
            // To redefine the True and False parsers, we need to rebuild the Fluid Primary expression parser.
            // Fluid defines a Primary expression as: primary => NUMBER | STRING | BOOLEAN | MEMBER

            // Reproduce the standard Fluid parsers that are internally defined by the default parser.
            var indexer = Between(LBracket, Primary, RBracket).Then <MemberSegment>(x => new IndexerSegment(x));

            var member = Identifier.Then <MemberSegment>(x => new IdentifierSegment(x)).And(
                ZeroOrMany(
                    Dot.SkipAnd(Identifier.Then <MemberSegment>(x => new IdentifierSegment(x)))
                    .Or(indexer)))
                         .Then(x =>
            {
                x.Item2.Insert(0, x.Item1);
                return(new MemberExpression(x.Item2));
            });

            // Redefine the Fluid keywords "true" and "false" as case-insensitive.
            var trueParser  = Terms.Text("true", caseInsensitive: true);
            var falseParser = Terms.Text("false", caseInsensitive: true);

            // Replace the default definition of the Fluid Primary parser.
            Primary.Parser = Number.Then <Expression>(x => new LiteralExpression(NumberValue.Create(x)))
                             .Or(String.Then <Expression>(x => new LiteralExpression(StringValue.Create(x.ToString()))))
                             .Or(trueParser.Then <Expression>(x => new LiteralExpression(BooleanValue.True)))
                             .Or(falseParser.Then <Expression>(x => new LiteralExpression(BooleanValue.False)))
                             .Or(member.Then <Expression>(x => x));
        }
예제 #17
0
 public IValue Evaluate()
 {
     if (Operator == "+")
     {
         return(Operand.Evaluate().ImplicitConversionToNumberValue());
     }
     if (Operator == "-")
     {
         return(NumberValue.Create(-Operand.Evaluate().ImplicitConversionToNumberValue().Value));
     }
     if (Operator == "~")
     {
         return(NumberValue.Create(~Operand.Evaluate().ImplicitConversionToNumberValue().BitwiseValue));
     }
     if (Operator == "!")
     {
         return(BoolValue.Create(!Operand.Evaluate().ImplicitConversionToBoolValue().Value));
     }
     if (Operator == "++")
     {
         return(NumberValue.Create(Operand.Evaluate().ImplicitConversionToNumberValue().Value + 1));
     }
     if (Operator == "--")
     {
         return(NumberValue.Create(Operand.Evaluate().ImplicitConversionToNumberValue().Value - 1));
     }
     throw new Exception("Internal error: no implementation for operator '" + Operator + "'"); // TODO: different exception type?
 }
예제 #18
0
        public override void ConfigureServices(IServiceCollection services)
        {
            services.Configure <TemplateOptions>(o =>
            {
                o.Scope.SetValue("Site", new ObjectValue(new LiquidSiteSettingsAccessor()));
                o.MemberAccessStrategy.Register <LiquidSiteSettingsAccessor, FluidValue>(async(obj, name, context) =>
                {
                    var liquidTemplateContext = (LiquidTemplateContext)context;

                    var siteService = liquidTemplateContext.Services.GetRequiredService <ISiteService>();
                    var site        = await siteService.GetSiteSettingsAsync();

                    FluidValue result = name switch
                    {
                        nameof(ISite.SiteName) => new StringValue(site.SiteName),
                        nameof(ISite.PageTitleFormat) => new StringValue(site.PageTitleFormat),
                        nameof(ISite.SiteSalt) => new StringValue(site.SiteSalt),
                        nameof(ISite.SuperUser) => new StringValue(site.SuperUser),
                        nameof(ISite.Calendar) => new StringValue(site.Calendar),
                        nameof(ISite.TimeZoneId) => new StringValue(site.TimeZoneId),
                        nameof(ISite.ResourceDebugMode) => new StringValue(site.ResourceDebugMode.ToString()),
                        nameof(ISite.UseCdn) => BooleanValue.Create(site.UseCdn),
                        nameof(ISite.CdnBaseUrl) => new StringValue(site.CdnBaseUrl),
                        nameof(ISite.PageSize) => NumberValue.Create(site.PageSize),
                        nameof(ISite.MaxPageSize) => NumberValue.Create(site.MaxPageSize),
                        nameof(ISite.MaxPagedCount) => NumberValue.Create(site.MaxPagedCount),
                        nameof(ISite.BaseUrl) => new StringValue(site.BaseUrl),
                        nameof(ISite.HomeRoute) => new ObjectValue(site.HomeRoute),
                        nameof(ISite.AppendVersion) => BooleanValue.Create(site.AppendVersion),
                        nameof(ISite.CacheMode) => new StringValue(site.CacheMode.ToString()),
                        nameof(ISite.Properties) => new ObjectValue(site.Properties),
                        _ => NilValue.Instance
                    };

                    return(result);
                });
            });

            services.AddScoped <ISetupEventHandler, SetupEventHandler>();
            services.AddScoped <IPermissionProvider, Permissions>();
            services.AddScoped <IAuthorizationHandler, SuperUserHandler>();

            services.AddRecipeExecutionStep <SettingsStep>();
            services.AddSingleton <ISiteService, SiteService>();

            // Site Settings editor
            services.AddScoped <IDisplayManager <ISite>, DisplayManager <ISite> >();
            services.AddScoped <IDisplayDriver <ISite>, DefaultSiteSettingsDisplayDriver>();
            services.AddScoped <IDisplayDriver <ISite>, ButtonsSettingsDisplayDriver>();
            services.AddScoped <INavigationProvider, AdminMenu>();

            services.AddScoped <ITimeZoneSelector, DefaultTimeZoneSelector>();

            services.AddTransient <IDeploymentSource, SiteSettingsDeploymentSource>();
            services.AddSingleton <IDeploymentStepFactory>(new DeploymentStepFactory <SiteSettingsDeploymentStep>());
            services.AddScoped <IDisplayDriver <DeploymentStep>, SiteSettingsDeploymentStepDriver>();

            services.AddScoped <IRecipeEnvironmentProvider, RecipeEnvironmentSiteNameProvider>();
        }
예제 #19
0
        public async ValueTask <FluidValue> ProcessAsync(FluidValue input, FilterArguments arguments, TemplateContext ctx)
        {
            var text = await GetTextAsync(input);

            var readTime = _readTimeCalculator.CalculateReadTime(text);

            return(NumberValue.Create(readTime));
        }
예제 #20
0
        public static FluidValue Modulo(FluidValue input, FilterArguments arguments, TemplateContext context)
        {
            if (arguments.Count == 0)
            {
                throw new ParseException("The filter 'modulo' requires an argument.");
            }

            return(NumberValue.Create(Convert.ToInt32(input.ToNumberValue()) % Convert.ToInt32(arguments.At(0).ToNumberValue())));
        }
        public override Value GetValue(UserItem item)
        {
            if (item.Item.CommunityRating.HasValue)
            {
                return(NumberValue.Create(item.Item.CommunityRating.GetValueOrDefault()));
            }

            return(Value.None);
        }
        public override Value GetValue(UserItem item)
        {
            if (item.Item.ProductionYear.HasValue)
            {
                return(NumberValue.Create(item.Item.ProductionYear.Value));
            }

            return(Value.None);
        }
예제 #23
0
        public static ValueTask <FluidValue> AmplifyInput(FluidValue input, FilterArguments arguments, TemplateContext ctx)
        {
            var threshold    = arguments.Count > 1 ? arguments.At(1).ToNumberValue() : 100;
            var inputFactor  = input.ToNumberValue();
            var amp          = arguments.At(0).ToNumberValue();
            var resultFactor = inputFactor > threshold ? inputFactor * amp : inputFactor * (threshold - amp);

            return(NumberValue.Create(resultFactor));
        }
예제 #24
0
        public void ModuloWithNoArgumentThrows()
        {
            var input = NumberValue.Create(6);

            var arguments = FilterArguments.Empty;
            var context   = new TemplateContext();

            Assert.Throws <ParseException>(() => NumberFilters.Modulo(input, arguments, context));
        }
예제 #25
0
        public override Value GetValue(UserItem item)
        {
            if (item.Item is Audio && item.Item.ParentIndexNumber.HasValue)
            {
                return(NumberValue.Create(item.Item.ParentIndexNumber.Value));
            }

            return(Value.None);
        }
예제 #26
0
 public override ValueTask <FluidValue> GetValueAsync(string name, TemplateContext context)
 {
     return(name switch
     {
         "size" => NumberValue.Create(123),
         "first" => NumberValue.Create(456),
         "last" => NumberValue.Create(789),
         _ => NilValue.Instance
     });
예제 #27
0
        public static FluidValue Times(FluidValue input, FilterArguments arguments, TemplateContext context)
        {
            var first            = arguments.At(0);
            var resultIsIntegral =
                input is NumberValue inputNumber && inputNumber.IsIntegral &&
                first is NumberValue firstNumber && firstNumber.IsIntegral;

            return(NumberValue.Create(input.ToNumberValue() * first.ToNumberValue(), resultIsIntegral));
        }
        public override Value GetValue(UserItem item)
        {
            if (item.Item.RunTimeTicks.HasValue)
            {
                return(NumberValue.Create(GetMinutes(item.Item.RunTimeTicks.GetValueOrDefault())));
            }

            return(Value.None);
        }
예제 #29
0
        public void Truncate(string input, int size, string output)
        {
            var source    = new StringValue(input);
            var arguments = new FilterArguments().Add(NumberValue.Create(size));
            var context   = new TemplateContext();
            var result    = StringFilters.Truncate(source, arguments, context);

            Assert.Equal(output, result.Result.ToStringValue());
        }
예제 #30
0
        public override Value GetValue(UserItem item)
        {
            if (item.Item is Episode && item.Item.IndexNumber.HasValue)
            {
                return(NumberValue.Create(item.Item.IndexNumber.Value));
            }

            return(Value.None);
        }