static CommonLiquidContextHandler()
 {
     FluidValue.SetTypeMapping <ExpandoObject>(x => new ObjectValue(x));
     FluidValue.SetTypeMapping <JObject>(o => new ObjectValue(o));
     FluidValue.SetTypeMapping <JValue>(o => FluidValue.Create(o.Value));
 }
Exemple #2
0
 public static TemplateContext SetValue(this TemplateContext context, string name, object value)
 {
     return(context.SetValue(name, FluidValue.Create(value, context.Options)));
 }
Exemple #3
0
        private static async ValueTask WriteJson(Utf8JsonWriter writer, FluidValue input, TemplateContext ctx, HashSet <object> stack = null)
        {
            switch (input.Type)
            {
            case FluidValues.Array:
                writer.WriteStartArray();
                foreach (var item in input.Enumerate())
                {
                    await WriteJson(writer, item, ctx);
                }

                writer.WriteEndArray();
                break;

            case FluidValues.Boolean:
                writer.WriteBooleanValue(input.ToBooleanValue());
                break;

            case FluidValues.Nil:
                writer.WriteNullValue();
                break;

            case FluidValues.Number:
                writer.WriteNumberValue(input.ToNumberValue());
                break;

            case FluidValues.Dictionary:
                if (input.ToObjectValue() is IFluidIndexable dic)
                {
                    writer.WriteStartObject();
                    foreach (var key in dic.Keys)
                    {
                        writer.WritePropertyName(key);
                        if (dic.TryGetValue(key, out var value))
                        {
                            await WriteJson(writer, value, ctx);
                        }
                        else
                        {
                            await WriteJson(writer, NilValue.Instance, ctx);
                        }
                    }

                    writer.WriteEndObject();
                }

                break;

            case FluidValues.Object:
                var obj = input.ToObjectValue();
                if (obj != null)
                {
                    writer.WriteStartObject();
                    var type       = obj.GetType();
                    var properties = type.GetProperties();
                    var strategy   = ctx.Options.MemberAccessStrategy;

                    var conv = strategy.MemberNameStrategy;
                    foreach (var property in properties)
                    {
                        var name   = conv(property);
                        var access = strategy.GetAccessor(type, name);
                        if (access == null)
                        {
                            continue;
                        }

                        object value;
                        if (access is IAsyncMemberAccessor asyncMemberAccessor)
                        {
                            value = await asyncMemberAccessor.GetAsync(obj, name, ctx);
                        }
                        else
                        {
                            value = access.Get(obj, name, ctx);
                        }

                        stack ??= new HashSet <object>();
                        if (stack.Contains(value))
                        {
                            value = "circular reference detected.";
                        }

                        var fluidValue = FluidValue.Create(value, ctx.Options);
                        if (fluidValue.IsNil())
                        {
                            continue;
                        }

                        writer.WritePropertyName(name);
                        stack.Add(obj);
                        await WriteJson(writer, fluidValue, ctx, stack);

                        stack.Remove(obj);
                    }

                    writer.WriteEndObject();
                }

                break;

            case FluidValues.DateTime:
                var objValue = input.ToObjectValue();
                if (objValue is DateTime dateTime)
                {
                    writer.WriteStringValue(dateTime);
                }
                else if (objValue is DateTimeOffset dateTimeOffset)
                {
                    writer.WriteStringValue(dateTimeOffset);
                }
                else
                {
                    writer.WriteStringValue(Convert.ToDateTime(objValue));
                }
                break;

            case FluidValues.String:
                writer.WriteStringValue(input.ToStringValue());
                break;

            case FluidValues.Blank:
                writer.WriteStringValue(string.Empty);
                break;

            case FluidValues.Empty:
                writer.WriteStringValue(string.Empty);
                break;

            default:
                throw new NotSupportedException("Unrecognized FluidValue");
            }
        }
Exemple #4
0
        public FilterArguments Add(object value)
        {
            _positional.Add(FluidValue.Create(value));

            return(this);
        }
 private Task <FluidValue> ToFluidValue(IDictionary <string, object> dictionary, string key)
 {
     return(Task.FromResult(!dictionary.ContainsKey(key) ? default : FluidValue.Create(dictionary[key])));
 }
 static async ValueTask <FluidValue> Awaited(Task <IShape> task, TemplateOptions options)
 {
     return(FluidValue.Create(await task, options));
 }
Exemple #7
0
        public override void ConfigureServices(IServiceCollection services)
        {
            services.AddSingleton <IAnchorTag, ContentAnchorTag>();

            services.Configure <LiquidViewOptions>(o =>
            {
                o.LiquidViewParserConfiguration.Add(parser => parser.RegisterParserTag("contentitem", parser.ArgumentsListParser, ContentItemTag.WriteToAsync));
            });

            services.Configure <TemplateOptions>(o =>
            {
                o.MemberAccessStrategy.Register <ContentItem>();
                o.MemberAccessStrategy.Register <ContentElement>();
                o.MemberAccessStrategy.Register <ShapeViewModel <ContentItem> >();
                o.MemberAccessStrategy.Register <ContentTypePartDefinition>();
                o.MemberAccessStrategy.Register <ContentPartFieldDefinition>();
                o.MemberAccessStrategy.Register <ContentFieldDefinition>();
                o.MemberAccessStrategy.Register <ContentPartDefinition>();

                o.Filters.AddFilter("display_text", DisplayTextFilter.DisplayText);

                o.Scope.SetValue("Content", new ObjectValue(new LiquidContentAccessor()));
                o.MemberAccessStrategy.Register <LiquidContentAccessor, LiquidPropertyAccessor>("ContentItemId", (obj, context) =>
                {
                    var liquidTemplateContext = (LiquidTemplateContext)context;

                    return(new LiquidPropertyAccessor(liquidTemplateContext, async(contentItemId, context) =>
                    {
                        var contentManager = context.Services.GetRequiredService <IContentManager>();

                        return FluidValue.Create(await contentManager.GetAsync(contentItemId), context.Options);
                    }));
                });

                o.MemberAccessStrategy.Register <LiquidContentAccessor, LiquidPropertyAccessor>("ContentItemVersionId", (obj, context) =>
                {
                    var liquidTemplateContext = (LiquidTemplateContext)context;

                    return(new LiquidPropertyAccessor(liquidTemplateContext, async(contentItemVersionId, context) =>
                    {
                        var contentManager = context.Services.GetRequiredService <IContentManager>();

                        return FluidValue.Create(await contentManager.GetVersionAsync(contentItemVersionId), context.Options);
                    }));
                });

                o.MemberAccessStrategy.Register <LiquidContentAccessor, LiquidPropertyAccessor>("Latest", (obj, context) =>
                {
                    var liquidTemplateContext = (LiquidTemplateContext)context;

                    return(new LiquidPropertyAccessor(liquidTemplateContext, (name, context) =>
                    {
                        return GetContentByHandleAsync(context, name, true);
                    }));
                });

                o.MemberAccessStrategy.Register <LiquidContentAccessor, FluidValue>((obj, name, context) => GetContentByHandleAsync((LiquidTemplateContext)context, name));

                async Task <FluidValue> GetContentByHandleAsync(LiquidTemplateContext context, string handle, bool latest = false)
                {
                    var contentHandleManager = context.Services.GetRequiredService <IContentHandleManager>();

                    var contentItemId = await contentHandleManager.GetContentItemIdAsync(handle);

                    if (contentItemId == null)
                    {
                        return(NilValue.Instance);
                    }

                    var contentManager = context.Services.GetRequiredService <IContentManager>();

                    var contentItem = await contentManager.GetAsync(contentItemId, latest ? VersionOptions.Latest : VersionOptions.Published);
                    return(FluidValue.Create(contentItem, context.Options));
                }
            })
            .AddLiquidFilter <DisplayUrlFilter>("display_url")
            .AddLiquidFilter <BuildDisplayFilter>("shape_build_display")
            .AddLiquidFilter <ContentItemFilter>("content_item_id")
            .AddLiquidFilter <FullTextFilter>("full_text");

            services.AddContentManagement();
            services.AddContentManagementDisplay();
            services.AddScoped <IPermissionProvider, Permissions>();
            services.AddScoped <IPermissionProvider, ContentTypePermissions>();
            services.AddScoped <IAuthorizationHandler, ContentTypeAuthorizationHandler>();
            services.AddScoped <IShapeTableProvider, Shapes>();
            services.AddScoped <INavigationProvider, AdminMenu>();
            services.AddScoped <IContentDisplayDriver, ContentsDriver>();
            services.AddScoped <IContentHandler, ContentsHandler>();
            services.AddRecipeExecutionStep <ContentStep>();

            services.AddScoped <IContentItemIndexHandler, FullTextContentIndexHandler>();
            services.AddScoped <IContentItemIndexHandler, AspectsContentIndexHandler>();
            services.AddScoped <IContentItemIndexHandler, DefaultContentIndexHandler>();
            services.AddScoped <IContentHandleProvider, ContentItemIdHandleProvider>();
            services.AddScoped <IContentItemIndexHandler, ContentItemIndexCoordinator>();

            services.AddScoped <IDataMigration, Migrations>();

            // Common Part
            services.AddContentPart <CommonPart>()
            .UseDisplayDriver <DateEditorDriver>()
            .UseDisplayDriver <OwnerEditorDriver>();

            services.AddScoped <IContentTypePartDefinitionDisplayDriver, CommonPartSettingsDisplayDriver>();

            // FullTextAspect
            services.AddScoped <IContentTypeDefinitionDisplayDriver, FullTextAspectSettingsDisplayDriver>();
            services.AddScoped <IContentHandler, FullTextAspectContentHandler>();

            services.AddTagHelpers <ContentLinkTagHelper>();
            services.AddTagHelpers <ContentItemTagHelper>();
            services.Configure <AutorouteOptions>(options =>
            {
                if (options.GlobalRouteValues.Count == 0)
                {
                    options.GlobalRouteValues = new RouteValueDictionary
                    {
                        { "Area", "OrchardCore.Contents" },
                        { "Controller", "Item" },
                        { "Action", "Display" }
                    };

                    options.ContentItemIdKey          = "contentItemId";
                    options.ContainedContentItemIdKey = "containedContentItemId";
                    options.JsonPathKey = "jsonPath";
                }
            });

            services.AddScoped <IContentsAdminListQueryService, DefaultContentsAdminListQueryService>();

            services.AddScoped <IDisplayManager <ContentOptionsViewModel>, DisplayManager <ContentOptionsViewModel> >();
            services.AddScoped <IDisplayDriver <ContentOptionsViewModel>, ContentOptionsDisplayDriver>();

            services.AddScoped(typeof(IContentItemRecursionHelper <>), typeof(ContentItemRecursionHelper <>));

            services.AddSingleton <IContentsAdminListFilterParser>(sp =>
            {
                var filterProviders = sp.GetServices <IContentsAdminListFilterProvider>();
                var builder         = new QueryEngineBuilder <ContentItem>();
                foreach (var provider in filterProviders)
                {
                    provider.Build(builder);
                }

                var parser = builder.Build();

                return(new DefaultContentsAdminListFilterParser(parser));
            });

            services.AddTransient <IContentsAdminListFilterProvider, DefaultContentsAdminListFilterProvider>();
        }
Exemple #8
0
 public void SetValue(string name, object value)
 {
     SetValue(name, FluidValue.Create(value));
 }
Exemple #9
0
 static async ValueTask <FluidValue> Awaited(Task <IShape> task)
 {
     return(FluidValue.Create(await task));
 }
Exemple #10
0
        /// <summary>
        /// Registers a set of Liquid-style filter functions for use with the Fluid templating engine.
        /// The original filters are wrapped in a function with a Fluid-compatible signature so they can be called by Fluid.
        /// </summary>
        /// <param name="lavaFilterMethod">The MethodInfo object that describes the function.</param>
        /// <param name="filterName">The Lava name for the filter.</param>
        protected override void OnRegisterFilter(MethodInfo lavaFilterMethod, string filterName)
        {
            var lavaFilterMethodParameters = lavaFilterMethod.GetParameters();

            if (lavaFilterMethodParameters.Length == 0)
            {
                return;
            }

            var templateOptions = GetTemplateOptions();

            filterName = filterName ?? lavaFilterMethod.Name;

            // The first argument passed to the Lava filter is either the Lava Context or the template input.
            var hasContextParameter = lavaFilterMethodParameters[0].ParameterType == typeof(ILavaRenderContext);

            var firstParameterIndex = 1 + (hasContextParameter ? 1 : 0);

            // Define the Fluid-compatible filter function that will wrap the Lava filter method.
            ValueTask <FluidValue> fluidFilterFunction(FluidValue input, FilterArguments arguments, TemplateContext context)
            {
                var lavaFilterMethodArguments = new object[lavaFilterMethodParameters.Length];

                for (int i = 0; i < lavaFilterMethodParameters.Length; i++)
                {
                    FluidValue fluidFilterArgument = null;

                    // Get the value for the argument.
                    if (i == 0)
                    {
                        // If this is the first parameter, it may be a LavaContext or the input template.
                        if (hasContextParameter)
                        {
                            lavaFilterMethodArguments[0] = new FluidRenderContext(context);
                            continue;
                        }
                        else
                        {
                            fluidFilterArgument = input;
                        }
                    }
                    else if (i == 1 && hasContextParameter)
                    {
                        // If this is the second parameter, it must be the input template if the first parameter is a LavaContext.
                        fluidFilterArgument = input;
                    }
                    else if (arguments.Count > (i - firstParameterIndex))
                    {
                        // This parameter is a filter argument.
                        fluidFilterArgument = arguments.At(i - firstParameterIndex);
                    }

                    if (fluidFilterArgument == null && lavaFilterMethodParameters[i].IsOptional)
                    {
                        lavaFilterMethodArguments[i] = lavaFilterMethodParameters[i].DefaultValue;
                    }
                    else
                    {
                        lavaFilterMethodArguments[i] = GetLavaParameterArgumentFromFluidValue(fluidFilterArgument, lavaFilterMethodParameters[i].ParameterType);
                    }
                }

                try
                {
                    var result = lavaFilterMethod.Invoke(null, lavaFilterMethodArguments);

                    return(FluidValue.Create(result, templateOptions));
                }
                catch (TargetInvocationException ex)
                {
                    // Any exceptions thrown from the filter method are wrapped in a TargetInvocationException by the .NET framework.
                    // Rethrow the actual exception thrown by the filter, where possible.
                    throw ex.InnerException ?? ex;
                }
            }

            templateOptions.Filters.AddFilter(filterName, fluidFilterFunction);
        }
Exemple #11
0
        /// <summary>
        /// Register value converters for Types that are not natively handled by Fluid.
        /// </summary>
        private void RegisterValueConverters()
        {
            var templateOptions = GetTemplateOptions();

            /* [2021-06-24] DL
             * Value Converters can have a significant impact on rendering performance.
             * Wherever possible, a conversion function should:
             * 1. Process all conversions related to a specific Type domain, to avoid the need to execute similar code in multiple converters.
             * 2. Order from most to least frequently executed and least to most expensive execution time.
             * 3. Return a FluidValue as quickly as possible, to avoid executing subsequent value converters in the collection.
             */

            // TODO: Fluid executes value converters prior to internal converters.
            // Performance test this implementation with short-circuiting for basic types.
            templateOptions.ValueConverters.Add((value) =>
            {
                // If the value is an Enum, render the value name.
                if (value is Enum e)
                {
                    return(e.ToString());
                }

                // This converter cannot process the value.
                return(null);
            });

            // Substitute the default Fluid DateTimeValue with an implementation that renders in the General DateTime format
            // rather than in UTC format.
            templateOptions.ValueConverters.Add((value) =>
            {
                if (value is DateTime dt)
                {
                    // Assume that the DateTime is expressed in Rock time.
                    return(new LavaDateTimeValue(LavaDateTime.NewDateTimeOffset(dt.Ticks)));
                }
                else if (value is DateTimeOffset dto)
                {
                    return(new LavaDateTimeValue(dto));
                }

                // This converter cannot process the value.
                return(null);
            });

            // DBNull is required to process results from the Sql command.
            // If this type is not registered, Fluid throws some seemingly unrelated exceptions.
            templateOptions.ValueConverters.Add((value) => value is System.DBNull ? FluidValue.Create(null, templateOptions) : null);

            // Converter for a Dictionary with a non-string key type; wraps the dictionary in a proxy so it can be accessed
            // with a key that is not a string type.
            // If this converter is not registered, any attempt to access a non-standard dictionary by key returns a null value.
            templateOptions.ValueConverters.Add((value) =>
            {
                // If the value is not a dictionary, this converter is not applicable.
                if (!(value is IDictionary))
                {
                    return(value);
                }

                // If the value is a standard Liquid-compatible dictionary,
                // return the appropriate Fluid wrapper to short-circuit further conversion attempts.
                if (value is IDictionary <string, object> liquidDictionary)
                {
                    return(new DictionaryValue(new ObjectDictionaryFluidIndexable(liquidDictionary, _templateOptions)));
                }

                var valueType = value.GetType();

                // If the value is derived from LavaDataObject, no conversion is needed.
                if (typeof(LavaDataObject).IsAssignableFrom(valueType))
                {
                    return(value);
                }

                // If this is a generic dictionary with a string key type, no conversion is needed.
                if (valueType.IsGenericType &&
                    valueType.GetGenericTypeDefinition() == typeof(Dictionary <,>))
                {
                    var keyType = valueType.GetGenericArguments()[0];

                    if (keyType == typeof(string))
                    {
                        return(value);
                    }
                }

                // Wrap the dictionary in a proxy so it can be accessed with a key that is not a string type.
                return(new LavaDataObject(value));
            });
        }
Exemple #12
0
        public Task RenderingAsync(TemplateContext context)
        {
            context.LocalScope.SetValue("Queries", new LiquidPropertyAccessor(async name => FluidValue.Create(await _queryManager.GetQueryAsync(name))));

            return(Task.CompletedTask);
        }
        public async Task <IQueryResults> ExecuteQueryAsync(Query query, IDictionary <string, object> parameters)
        {
            var luceneQuery        = query as LuceneQuery;
            var luceneQueryResults = new LuceneQueryResults();

            await _luceneIndexProvider.SearchAsync(luceneQuery.Index, async searcher =>
            {
                var tokenizedContent = await _liquidTemplateManager.RenderStringAsync(luceneQuery.Template, _javaScriptEncoder, parameters.Select(x => new KeyValuePair <string, FluidValue>(x.Key, FluidValue.Create(x.Value, _templateOptions))));

                var parameterizedQuery = JObject.Parse(tokenizedContent);

                var analyzer             = _luceneAnalyzerManager.CreateAnalyzer(await _luceneIndexSettingsService.GetIndexAnalyzerAsync(luceneQuery.Index));
                var context              = new LuceneQueryContext(searcher, LuceneSettings.DefaultVersion, analyzer);
                var docs                 = await _queryService.SearchAsync(context, parameterizedQuery);
                luceneQueryResults.Count = docs.Count;

                if (luceneQuery.ReturnContentItems)
                {
                    // We always return an empty collection if the bottom lines queries have no results.
                    luceneQueryResults.Items = new List <ContentItem>();

                    // Load corresponding content item versions
                    var indexedContentItemVersionIds = docs.TopDocs.ScoreDocs.Select(x => searcher.Doc(x.Doc).Get("Content.ContentItem.ContentItemVersionId")).ToArray();
                    var dbContentItems = await _session.Query <ContentItem, ContentItemIndex>(x => x.ContentItemVersionId.IsIn(indexedContentItemVersionIds)).ListAsync();

                    // Reorder the result to preserve the one from the lucene query
                    if (dbContentItems.Any())
                    {
                        var dbContentItemVersionIds = dbContentItems.ToDictionary(x => x.ContentItemVersionId, x => x);
                        var indexedAndInDB          = indexedContentItemVersionIds.Where(dbContentItemVersionIds.ContainsKey);
                        luceneQueryResults.Items    = indexedAndInDB.Select(x => dbContentItemVersionIds[x]).ToArray();
                    }
                }
                else
                {
                    var results = new List <JObject>();
                    foreach (var document in docs.TopDocs.ScoreDocs.Select(hit => searcher.Doc(hit.Doc)))
                    {
                        results.Add(new JObject(document.Select(x => new JProperty(x.Name, x.GetStringValue()))));
                    }

                    luceneQueryResults.Items = results;
                }
            });

            return(luceneQueryResults);
        }
 public static FluidValue FormatTimestampSec(FluidValue input, FilterArguments arguments, TemplateContext context)
 {
     return(FormatDate(input, x => FluidValue.Create(x.ToUnixTimeMilliseconds() / 1000)));
 }
Exemple #15
0
 public FilterArguments Add(string name, object value)
 {
     return(Add(name, FluidValue.Create(value)));
 }
Exemple #16
0
 public TemplateContext SetValue(string name, object value)
 {
     return(SetValue(name, FluidValue.Create(value)));
 }
Exemple #17
0
        public async Task DefaultReturnsDefaultIfNotDefinedOrEmptyOrFalse(object expected, object input, object @default, bool allowFalse)
        {
            var arguments = new FilterArguments()
                            .Add("default", FluidValue.Create(@default, TemplateOptions.Default))
                            .Add("allow_false", FluidValue.Create(allowFalse, TemplateOptions.Default));

            var context = new TemplateContext();
            var result  = await MiscFilters.Default("empty" == input as string?EmptyValue.Instance : FluidValue.Create(input, TemplateOptions.Default), arguments, context);

            Assert.Equal(expected, result.ToObjectValue());
        }
Exemple #18
0
        public static string parseRaw(string filePath)
        {
            Console.WriteLine("parseRaw - " + filePath);

            var siteConfig   = GlobalConfiguration.getConfiguration();
            var fileContents = WDHANFile.getFileContents(filePath);

            fileContents = Include.evalInclude(filePath); // Expand includes (must happen after layouts are retreived, as layouts can have includes)

            // When a property of a JObject value is accessed, try to look into its properties
            TemplateContext.GlobalMemberAccessStrategy.Register <JObject, object>((source, name) => source[name]);

            // Convert JToken to FluidValue
            FluidValue.SetTypeMapping <JObject>(o => new ObjectValue(o));
            FluidValue.SetTypeMapping <JValue>(o => FluidValue.Create(o.Value));


            var siteModel = JObject.Parse(File.ReadAllText("./_config.json"));
            var dataSet   = JObject.Parse(File.ReadAllText(siteConfig.source + "/temp/_data.json"));
            var pageModel = WDHANFile.parseFrontMatter(filePath);

            Console.WriteLine("fileContents!!!" + filePath + ":\n" + fileContents);

            try
            {
                if (FluidTemplate.TryParse(fileContents, out var template))
                {
                    var context = new TemplateContext();
                    context.CultureInfo = new CultureInfo(siteConfig.culture);

                    siteModel.Merge(dataSet, new JsonMergeSettings {
                        MergeArrayHandling = MergeArrayHandling.Union
                    });
                    context.SetValue("site", siteModel);
                    context.SetValue("page", pageModel);

                    foreach (var collection in siteConfig.collections)
                    {
                        if (File.Exists(siteConfig.source + "/temp/_" + collection + "/_entries.json"))
                        {
                            var collectionModel = JObject.Parse(File.ReadAllText(siteConfig.source + "/_" + collection + "/_config.json"));
                            collectionModel.Merge(JObject.Parse(File.ReadAllText(siteConfig.source + "/temp/_" + collection + "/_entries.json")), new JsonMergeSettings {
                                MergeArrayHandling = MergeArrayHandling.Union
                            });
                            context.SetValue(collection, collectionModel);
                        }
                    }
                    Console.WriteLine("DONE!!! - " + filePath + " \n" + template.Render(context));
                    return(template.Render(context));
                }
                else
                {
                    Console.WriteLine("ERROR: Could not parse Liquid context for file " + filePath + ".");
                    Console.WriteLine("TryParse error:\n" + fileContents);
                    return(fileContents);
                }
            }
            catch (ArgumentNullException ex)
            {
                Console.WriteLine("File " + filePath + " has no Liquid context to parse.\n" + ex.ToString());
                return(fileContents);
            }
        }