diff --git a/src/Our.Umbraco.FullTextSearch.SchemaGenerator/AppSettings.cs b/src/Our.Umbraco.FullTextSearch.SchemaGenerator/AppSettings.cs new file mode 100644 index 0000000..8c8a923 --- /dev/null +++ b/src/Our.Umbraco.FullTextSearch.SchemaGenerator/AppSettings.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +using Our.Umbraco.FullTextSearch.Options; + +namespace Our.Umbraco.FullTextSearch.SchemaGenerator +{ + internal class AppSettings + { + public UmbracoDefinition Umbraco { get; set; } + + /// + /// Configuration of settings + /// + internal class UmbracoDefinition + { + /// + /// FullTextSearch settings + /// + public FullTextSearchOptions FullTextSearch { get; set; } + + } + } + +} diff --git a/src/Our.Umbraco.FullTextSearch.SchemaGenerator/Options.cs b/src/Our.Umbraco.FullTextSearch.SchemaGenerator/Options.cs new file mode 100644 index 0000000..3c0aa26 --- /dev/null +++ b/src/Our.Umbraco.FullTextSearch.SchemaGenerator/Options.cs @@ -0,0 +1,12 @@ +using CommandLine; + +namespace Our.Umbraco.FullTextSearch.SchemaGenerator +{ + internal class Options + { + [Option('o', "outputFile", Required = false, + HelpText = "", + Default = "Our.Umbraco.FullTextSearch\\appsettings-schema.umbraco-fulltextsearch.json")] + public string OutputFile { get; set; } + } +} diff --git a/src/Our.Umbraco.FullTextSearch.SchemaGenerator/Our.Umbraco.FullTextSearch.SchemaGenerator.csproj b/src/Our.Umbraco.FullTextSearch.SchemaGenerator/Our.Umbraco.FullTextSearch.SchemaGenerator.csproj new file mode 100644 index 0000000..bc2a752 --- /dev/null +++ b/src/Our.Umbraco.FullTextSearch.SchemaGenerator/Our.Umbraco.FullTextSearch.SchemaGenerator.csproj @@ -0,0 +1,27 @@ + + + + Exe + net7.0 + + + + + + + + + + + + + bin\Release\$(TargetFramework)\Our.Umbraco.FullTextSearch.SchemaGenerator.xml + + + + + + + + + diff --git a/src/Our.Umbraco.FullTextSearch.SchemaGenerator/Program.cs b/src/Our.Umbraco.FullTextSearch.SchemaGenerator/Program.cs new file mode 100644 index 0000000..64f053b --- /dev/null +++ b/src/Our.Umbraco.FullTextSearch.SchemaGenerator/Program.cs @@ -0,0 +1,46 @@ +using System; +using System.IO; +using System.Threading.Tasks; +using CommandLine; + +using NPoco.fastJSON; + +namespace Our.Umbraco.FullTextSearch.SchemaGenerator +{ + /// + /// Generate the JSON Schema file for Full Text Search. + /// just like in the Umbraco Core - https://github.com/umbraco/Umbraco-CMS/tree/v9/contrib/src/JsonSchema + /// #h5yr Kevin Jump! + /// + internal class Program + { + public static async Task Main(string[] args) + { + try + { + await Parser.Default.ParseArguments(args) + .WithParsedAsync(Execute); + } + catch (Exception e) + { + Console.WriteLine(e); + throw; + } + } + + private static async Task Execute(Options options) + { + var generator = new SchemaGenerator(); + + var schema = generator.Generate(); + + var path = Path.GetFullPath(Path.Combine(Environment.CurrentDirectory, options.OutputFile)); + Console.WriteLine("Path to use {0}", path); + Directory.CreateDirectory(Path.GetDirectoryName(path)); + Console.WriteLine("Ensured directory exists"); + await File.WriteAllTextAsync(path, schema); + + Console.WriteLine("File written at {0}", path); + } + } +} diff --git a/src/Our.Umbraco.FullTextSearch.SchemaGenerator/SchemaGenerator.cs b/src/Our.Umbraco.FullTextSearch.SchemaGenerator/SchemaGenerator.cs new file mode 100644 index 0000000..3c7368f --- /dev/null +++ b/src/Our.Umbraco.FullTextSearch.SchemaGenerator/SchemaGenerator.cs @@ -0,0 +1,84 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics.Contracts; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; +using Newtonsoft.Json.Linq; +using Newtonsoft.Json.Serialization; + +using NJsonSchema.Generation; + +namespace Our.Umbraco.FullTextSearch.SchemaGenerator +{ + internal class SchemaGenerator + { + private readonly JsonSchemaGenerator _schemaGenerator; + + public SchemaGenerator() + { + _schemaGenerator = new JsonSchemaGenerator( + new FullTextSearchSchemaGeneratorSettings()); + } + + public string Generate() + { + var schema = GenerateFullTextSearchSchema(); + return schema.ToString(); + } + + private JObject GenerateFullTextSearchSchema() + { + var schema = _schemaGenerator.Generate(typeof(AppSettings)); + return JsonConvert.DeserializeObject(schema.ToJson()); + } + + } + + internal class FullTextSearchSchemaGeneratorSettings : JsonSchemaGeneratorSettings + { + public FullTextSearchSchemaGeneratorSettings() + { + AlwaysAllowAdditionalObjectProperties = true; + SerializerSettings = new JsonSerializerSettings() + { + ContractResolver = new WritablePropertiesOnlyResolver(), + }; + DefaultReferenceTypeNullHandling = ReferenceTypeNullHandling.NotNull; + SchemaNameGenerator = new NamespacePrefixedSchemaNameGenerator(); + SerializerSettings.Converters.Add(new StringEnumConverter()); + IgnoreObsoleteProperties = true; + GenerateExamples = true; + } + + private class WritablePropertiesOnlyResolver : DefaultContractResolver + { + protected override IList CreateProperties(Type type, MemberSerialization memberSerialization) + { + IList props = base.CreateProperties(type, memberSerialization); + var result = props.Where(p => p.Writable).ToList(); + result.ForEach(x => x.PropertyName = ToPascalCase(x.PropertyName)); + return result; + } + + private string ToPascalCase(string str) + { + if (!string.IsNullOrEmpty(str)) + { + return char.ToUpperInvariant(str[0]) + str.Substring(1); + } + + return str; + + } + } + } + + internal class NamespacePrefixedSchemaNameGenerator : DefaultSchemaNameGenerator + { + public override string Generate(Type type) => type.Namespace.Replace(".", string.Empty) + base.Generate(type); + } +} diff --git a/src/Our.Umbraco.FullTextSearch.sln b/src/Our.Umbraco.FullTextSearch.sln index 6095cc5..6f0fdc6 100644 --- a/src/Our.Umbraco.FullTextSearch.sln +++ b/src/Our.Umbraco.FullTextSearch.sln @@ -31,6 +31,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Our.Umbraco.FullTextSearch. EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Our.Umbraco.FullTextSearch.Testsite13", "Testsites\Our.Umbraco.FullTextSearch.Testsite13\Our.Umbraco.FullTextSearch.Testsite13.csproj", "{9D899317-F4E7-4966-A03B-85FFA9B80E3C}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Our.Umbraco.FullTextSearch.SchemaGenerator", "Our.Umbraco.FullTextSearch.SchemaGenerator\Our.Umbraco.FullTextSearch.SchemaGenerator.csproj", "{560727BD-EFD0-4D19-ADB7-706C504E6E86}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -61,6 +63,10 @@ Global {9D899317-F4E7-4966-A03B-85FFA9B80E3C}.Debug|Any CPU.Build.0 = Debug|Any CPU {9D899317-F4E7-4966-A03B-85FFA9B80E3C}.Release|Any CPU.ActiveCfg = Release|Any CPU {9D899317-F4E7-4966-A03B-85FFA9B80E3C}.Release|Any CPU.Build.0 = Release|Any CPU + {560727BD-EFD0-4D19-ADB7-706C504E6E86}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {560727BD-EFD0-4D19-ADB7-706C504E6E86}.Debug|Any CPU.Build.0 = Debug|Any CPU + {560727BD-EFD0-4D19-ADB7-706C504E6E86}.Release|Any CPU.ActiveCfg = Release|Any CPU + {560727BD-EFD0-4D19-ADB7-706C504E6E86}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/src/Our.Umbraco.FullTextSearch/Options/FullTextSearchOptions.cs b/src/Our.Umbraco.FullTextSearch/Options/FullTextSearchOptions.cs index 1ecb490..6d9a198 100644 --- a/src/Our.Umbraco.FullTextSearch/Options/FullTextSearchOptions.cs +++ b/src/Our.Umbraco.FullTextSearch/Options/FullTextSearchOptions.cs @@ -6,23 +6,56 @@ namespace Our.Umbraco.FullTextSearch.Options; public class FullTextSearchOptions { + /// + /// Indexing the full text content is by default enabled, but you can disable it by setting `Enabled` to false. + /// [JsonProperty("enabled")] public bool Enabled { get; set; } = true; + + /// + /// The `DefaultTitleField` node contains the name of the field containing the title of the page in the index. The default value is `nodeName`. You can also override this when searching. + /// [JsonProperty("defaultTitleField")] public string DefaultTitleField { get; set; } = "nodeName"; + [Obsolete("Use RenderingActiveKey instead")] [JsonProperty("indexingActiveKey")] public string IndexingActiveKey { get; set; } = "FullTextRenderingActive"; + + /// + /// When rendering, FullTextSearch adds the value of `RenderingActiveKey` as the value of a Request header named `X-Umbraco-FullTextSearch`, so you can use that to send different content to the renderer. The default value is FullTextRenderingActive. You can also use the `IsRenderingActive` helper method, in your views, to determine whether or not FullTextSearch is rendering the page. You can use this to exclude parts of the views from the content being rendered/indexed. + /// [JsonProperty("renderingActiveKey")] public string RenderingActiveKey { get; set; } = "FullTextRenderingActive"; + + /// + /// By default, all nodes with a template will be cached and indexed. You can control which nodes are being indexed, by adding the aliases of the disallowed content type aliases here. + /// [JsonProperty("disallowedContentTypeAliases")] public List DisallowedContentTypeAliases { get; set; } = new List(); + + + /// + /// By default, all nodes with a template will be cached and indexed. You can control which nodes are being indexed, by adding the aliases of the properties containing a true/false editor, to control whether or not to include a node. A true value means it will be disallowed. + /// [JsonProperty("disallowedPropertyAliases")] public List DisallowedPropertyAliases { get; set; } = new List(); + + /// + /// Add specific Xpaths to remove from the rendered content. Using this, you can ie. remove scripts (`//script`) or the head area ('//head') of the page. + /// [JsonProperty("xPathsToRemove")] public List XPathsToRemove { get; set; } = new List(); + + /// + /// Field name to use in ExternalIndex for rendered full text content of pages. + /// [JsonProperty("fullTextContentField")] public string FullTextContentField { get; set; } = "FullTextContent"; + + /// + /// Field name to use for Full Text Searchs path field, used for determining hierarchy between pages. + /// [JsonProperty("fullTextPathField")] public string FullTextPathField { get; set; } = "FullTextPath"; diff --git a/src/Our.Umbraco.FullTextSearch/appsettings-schema.umbraco-fulltextsearch.json b/src/Our.Umbraco.FullTextSearch/appsettings-schema.umbraco-fulltextsearch.json new file mode 100644 index 0000000..1026168 --- /dev/null +++ b/src/Our.Umbraco.FullTextSearch/appsettings-schema.umbraco-fulltextsearch.json @@ -0,0 +1,67 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "OurUmbracoFullTextSearchSchemaGeneratorAppSettings", + "type": "object", + "properties": { + "Umbraco": { + "$ref": "#/definitions/OurUmbracoFullTextSearchSchemaGeneratorUmbracoDefinition" + } + }, + "definitions": { + "OurUmbracoFullTextSearchSchemaGeneratorUmbracoDefinition": { + "type": "object", + "description": "Configuration of settings", + "properties": { + "FullTextSearch": { + "description": "FullTextSearch settings", + "oneOf": [ + { + "$ref": "#/definitions/OurUmbracoFullTextSearchOptionsFullTextSearchOptions" + } + ] + } + } + }, + "OurUmbracoFullTextSearchOptionsFullTextSearchOptions": { + "type": "object", + "properties": { + "Enabled": { + "type": "boolean" + }, + "DefaultTitleField": { + "type": "string" + }, + "RenderingActiveKey": { + "type": "string" + }, + "DisallowedContentTypeAliases": { + "type": "array", + "items": { + "type": "string" + } + }, + "DisallowedPropertyAliases": { + "type": "array", + "items": { + "type": "string" + } + }, + "XPathsToRemove": { + "type": "array", + "items": { + "type": "string" + } + }, + "FullTextContentField": { + "type": "string" + }, + "FullTextPathField": { + "type": "string" + }, + "HighlightPattern": { + "type": "string" + } + } + } + } +} \ No newline at end of file diff --git a/src/Our.Umbraco.FullTextSearch/buildTransitive/Our.Umbraco.FullTextSearch.props b/src/Our.Umbraco.FullTextSearch/buildTransitive/Our.Umbraco.FullTextSearch.props new file mode 100644 index 0000000..06db5c0 --- /dev/null +++ b/src/Our.Umbraco.FullTextSearch/buildTransitive/Our.Umbraco.FullTextSearch.props @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file