static CommonLiquidContextHandler() { FluidValue.SetTypeMapping <ExpandoObject>(x => new ObjectValue(x)); FluidValue.SetTypeMapping <JObject>(o => new ObjectValue(o)); FluidValue.SetTypeMapping <JValue>(o => FluidValue.Create(o.Value)); }
public static TemplateContext SetValue(this TemplateContext context, string name, object value) { return(context.SetValue(name, FluidValue.Create(value, context.Options))); }
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"); } }
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)); }
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>(); }
public void SetValue(string name, object value) { SetValue(name, FluidValue.Create(value)); }
static async ValueTask <FluidValue> Awaited(Task <IShape> task) { return(FluidValue.Create(await task)); }
/// <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); }
/// <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)); }); }
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))); }
public FilterArguments Add(string name, object value) { return(Add(name, FluidValue.Create(value))); }
public TemplateContext SetValue(string name, object value) { return(SetValue(name, FluidValue.Create(value))); }
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()); }
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); } }