From 149b25c3ea9f2e11e61d36bf1f9a32c2bb1c36ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pauli=20=C3=98ster=C3=B8?= Date: Tue, 14 Apr 2020 19:03:39 +0200 Subject: [PATCH 01/20] Usage of JsonConvert.SerializeObject should not rely on default JsonSerializerSettings Fixes Issue 733 --- .../Core/Serialization/CompositeJsonSerializer.cs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/Composite/Core/Serialization/CompositeJsonSerializer.cs b/Composite/Core/Serialization/CompositeJsonSerializer.cs index 3a224715e8..728a1eb726 100644 --- a/Composite/Core/Serialization/CompositeJsonSerializer.cs +++ b/Composite/Core/Serialization/CompositeJsonSerializer.cs @@ -40,7 +40,7 @@ public static string Serialize(object obj) { TypeNameAssemblyFormat = System.Runtime.Serialization.Formatters.FormatterAssemblyStyle.Simple, TypeNameHandling = TypeNameHandling.Auto, - Converters = {new JsonTypeConverter()}, + Converters = { new JsonTypeConverter() }, Binder = CompositeSerializationBinder.Instance }); @@ -59,7 +59,7 @@ public static string SerializeObject(object obj) TypeNameAssemblyFormat = System.Runtime.Serialization.Formatters.FormatterAssemblyStyle.Simple, TypeNameHandling = TypeNameHandling.Objects, Formatting = Formatting.None, - Converters = {new JsonTypeConverter()}, + Converters = { new JsonTypeConverter() }, Binder = CompositeSerializationBinder.Instance }); @@ -79,8 +79,10 @@ public static string SerializePartial(object obj, IEnumerable propertyNa return SerializeObject(obj); } - var serializedData = - JsonConvert.SerializeObject(obj, new PartialJsonConvertor(propertyNames, obj.GetType())); + var serializedData = JsonConvert.SerializeObject(obj, new JsonSerializerSettings + { + Converters = { new PartialJsonConvertor(propertyNames, obj.GetType()) } + }); return serializedData; } From 61630fa52f4fe8dac678db0263d5a89aa7d5ad1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pauli=20=C3=98ster=C3=B8?= Date: Tue, 14 Apr 2020 19:08:01 +0200 Subject: [PATCH 02/20] Fixed spelling --- .../Serialization/CompositeJsonSerializer.cs | 29 ++++++++++--------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/Composite/Core/Serialization/CompositeJsonSerializer.cs b/Composite/Core/Serialization/CompositeJsonSerializer.cs index 728a1eb726..c175b2ceb2 100644 --- a/Composite/Core/Serialization/CompositeJsonSerializer.cs +++ b/Composite/Core/Serialization/CompositeJsonSerializer.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Reflection; using System.Runtime.Serialization; +using System.Runtime.Serialization.Formatters; using System.Security; using Composite.C1Console.Security; using Composite.Core.Types; @@ -20,7 +21,7 @@ public static class CompositeJsonSerializer private const string HashKeyString = "meta:hash"; /// - /// Check if string is serilized with JsonSerializer + /// Check if string is serialized with JsonSerializer /// /// /// @@ -33,12 +34,12 @@ public static bool IsJsonSerialized(string str) /// Serialize with automatic type name handling /// /// Object to serialize - /// seialized string + /// serialized string public static string Serialize(object obj) { var serializedData = JsonConvert.SerializeObject(obj, new JsonSerializerSettings { - TypeNameAssemblyFormat = System.Runtime.Serialization.Formatters.FormatterAssemblyStyle.Simple, + TypeNameAssemblyFormat = FormatterAssemblyStyle.Simple, TypeNameHandling = TypeNameHandling.Auto, Converters = { new JsonTypeConverter() }, Binder = CompositeSerializationBinder.Instance @@ -51,12 +52,12 @@ public static string Serialize(object obj) /// Serialize with json object structure type name handling /// /// Object to serialize - /// seialized string + /// serialized string public static string SerializeObject(object obj) { var serializedData = JsonConvert.SerializeObject(obj, new JsonSerializerSettings { - TypeNameAssemblyFormat = System.Runtime.Serialization.Formatters.FormatterAssemblyStyle.Simple, + TypeNameAssemblyFormat = FormatterAssemblyStyle.Simple, TypeNameHandling = TypeNameHandling.Objects, Formatting = Formatting.None, Converters = { new JsonTypeConverter() }, @@ -71,7 +72,7 @@ public static string SerializeObject(object obj) /// /// Object to serialize /// List of properties to be serialized - /// seialized string + /// serialized string public static string SerializePartial(object obj, IEnumerable propertyNames) { if (propertyNames == null) @@ -94,7 +95,7 @@ public static string SerializePartial(object obj, IEnumerable propertyNa /// /// Object to serialize /// To calculate the hash sign - /// seialized string + /// serialized string public static string Serialize(object obj, bool shouldSign) { var type = obj.GetType(); @@ -125,7 +126,7 @@ public static string Serialize(object obj, bool shouldSign) /// /// Deserialize string into object with specified type /// - /// Serilaized string + /// Serialized string /// Type of returned object /// The object public static T Deserialize(string str) @@ -141,7 +142,7 @@ public static T Deserialize(string str) /// /// Deserialize strings into object with specified type by merging them together /// - /// Serilaized string + /// Serialized string /// Type of returned object /// The object public static T Deserialize(params string[] strs) @@ -173,7 +174,7 @@ public static T Deserialize(params string[] strs) /// /// Deserialize string into object /// - /// Serilaized string + /// Serialized string /// The object public static object Deserialize(string str) { @@ -188,13 +189,13 @@ public static object Deserialize(string str) /// /// Deserialize strings into object with specified type from a hash signed wrapper /// - /// Serilaized string + /// Serialized string /// Is signed /// Type of returned object /// The object public static T Deserialize(string str, bool isSigned) { - var legacyStyleSerilized = str.StartsWith("{\"" + ObjectKeyString + "\":\""); + var legacyStyleSerialized = str.StartsWith("{\"" + ObjectKeyString + "\":\""); string obj; var hash = 0; @@ -209,7 +210,7 @@ public static T Deserialize(string str, bool isSigned) throw new SerializationException(); } - if (legacyStyleSerilized) + if (legacyStyleSerialized) { obj = str.GetValue(ObjectKeyString); } @@ -299,7 +300,7 @@ public override void WriteJson(JsonWriter writer, object value, JsonSerializer s { TypeNameAssemblyFormat = System.Runtime.Serialization.Formatters.FormatterAssemblyStyle.Simple, TypeNameHandling = TypeNameHandling.Objects, - Converters = {new JsonTypeConverter()} + Converters = { new JsonTypeConverter() } }; foreach (var property in _propertyNames) From beea1e112ea5d7c7b8796e25f767638af77c5354 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pauli=20=C3=98ster=C3=B8?= Date: Tue, 14 Apr 2020 19:14:31 +0200 Subject: [PATCH 03/20] Formatting --- .../Serialization/CompositeJsonSerializer.cs | 58 +++++++++++-------- 1 file changed, 33 insertions(+), 25 deletions(-) diff --git a/Composite/Core/Serialization/CompositeJsonSerializer.cs b/Composite/Core/Serialization/CompositeJsonSerializer.cs index c175b2ceb2..884f780050 100644 --- a/Composite/Core/Serialization/CompositeJsonSerializer.cs +++ b/Composite/Core/Serialization/CompositeJsonSerializer.cs @@ -88,8 +88,6 @@ public static string SerializePartial(object obj, IEnumerable propertyNa return serializedData; } - - /// /// Serialize with a wrapper containing the serialized object, its hash sign and its type /// @@ -99,9 +97,9 @@ public static string SerializePartial(object obj, IEnumerable propertyNa public static string Serialize(object obj, bool shouldSign) { var type = obj.GetType(); - var methodInfo = type.GetMethod("Serialize"); string serializedData; + var methodInfo = type.GetMethod("Serialize"); if (methodInfo == null) { serializedData = Serialize(obj); @@ -117,9 +115,9 @@ public static string Serialize(object obj, bool shouldSign) ? serializedData.Substring(1, serializedData.Length - 2) : $@"""{ObjectKeyString}"":""{serializedData}"""; - return "{" + serializedProperties - + $@",""{TypeKeyString}"":""{GetSerializedTypeName(type)}""" - + (shouldSign ? $@",""{HashKeyString}"":""{hash}""" : "") + return "{" + serializedProperties + + $@",""{TypeKeyString}"":""{GetSerializedTypeName(type)}""" + + (shouldSign ? $@",""{HashKeyString}"":""{hash}""" : "") + "}"; } @@ -136,6 +134,7 @@ public static T Deserialize(string str) TypeNameHandling = TypeNameHandling.Auto, Binder = CompositeSerializationBinder.Instance }); + return obj; } @@ -148,10 +147,12 @@ public static T Deserialize(string str) public static T Deserialize(params string[] strs) { var combinedObj = new JObject(); + var mergeSettings = new JsonMergeSettings { MergeArrayHandling = MergeArrayHandling.Union }; + try { foreach (var s in strs) @@ -163,11 +164,13 @@ public static T Deserialize(params string[] strs) { throw new ArgumentException("Cannot merge arguments into one"); } + var obj = JsonConvert.DeserializeObject(combinedObj.ToString(), new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.Auto, Binder = CompositeSerializationBinder.Instance }); + return obj; } @@ -183,6 +186,7 @@ public static object Deserialize(string str) TypeNameHandling = TypeNameHandling.Objects, Binder = CompositeSerializationBinder.Instance }); + return obj; } @@ -201,13 +205,17 @@ public static T Deserialize(string str, bool isSigned) var hash = 0; var type = TypeManager.TryGetType(str.GetValue(TypeKeyString)); - if (type==null) + if (type == null) + { throw new SerializationException(); + } if (isSigned) { - if(!int.TryParse(str.GetValue(HashKeyString),out hash)) + if (!int.TryParse(str.GetValue(HashKeyString), out hash)) + { throw new SerializationException(); + } } if (legacyStyleSerialized) @@ -226,7 +234,8 @@ public static T Deserialize(string str, bool isSigned) throw new SecurityException($"Serialized {typeof(T).FullName} is tampered"); } } - MethodInfo methodInfo = type.GetMethod("Deserialize", BindingFlags.Public | BindingFlags.Static); + + var methodInfo = type.GetMethod("Deserialize", BindingFlags.Public | BindingFlags.Static); if (methodInfo == null) { return Deserialize(obj); @@ -234,9 +243,10 @@ public static T Deserialize(string str, bool isSigned) if (!(typeof(T).IsAssignableFrom(methodInfo.ReturnType))) { - string typeName = str.GetValue(TypeKeyString); - Log.LogWarning("CompositeJsonSerializer", string.Format("The action {0} is missing a public static Deserialize method taking a string as parameter and returning an {1}", typeName, typeof(T))); - throw new InvalidOperationException(string.Format("The token {0} is missing a public static Deserialize method taking a string as parameter and returning an {1}", typeName, typeof(T))); + var typeName = str.GetValue(TypeKeyString); + Log.LogWarning("CompositeJsonSerializer", $"The action {typeName} is missing a public static Deserialize method taking a string as parameter and returning an {typeof(T)}"); + + throw new InvalidOperationException($"The token {typeName} is missing a public static Deserialize method taking a string as parameter and returning an {typeof(T)}"); } return (T)methodInfo.Invoke(null, new object[] { obj }); @@ -244,10 +254,11 @@ public static T Deserialize(string str, bool isSigned) private static string GetValue(this string str, string key) { - var searchterm = "\"" + key + "\":\""; - var valueStartIndex = str.LastIndexOf(searchterm,StringComparison.InvariantCulture) + searchterm.Length; - var valueLength = str.IndexOf("\"", valueStartIndex,StringComparison.InvariantCulture) - valueStartIndex; + var searchTerm = "\"" + key + "\":\""; + var valueStartIndex = str.LastIndexOf(searchTerm, StringComparison.InvariantCulture) + searchTerm.Length; + var valueLength = str.IndexOf("\"", valueStartIndex, StringComparison.InvariantCulture) - valueStartIndex; var value = str.Substring(valueStartIndex, valueLength); + return value; } @@ -260,12 +271,11 @@ private class JsonTypeConverter : JsonConverter { public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { - var type = (Type) value; + var type = (Type)value; writer.WriteValue(GetSerializedTypeName(type)); } - public override object ReadJson(JsonReader reader, Type objectType, object existingValue, - JsonSerializer serializer) + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { throw new NotImplementedException(); } @@ -278,7 +288,6 @@ public override bool CanConvert(Type objectType) } } - private class PartialJsonConvertor : JsonConverter { private readonly IEnumerable _propertyNames; @@ -298,7 +307,7 @@ public override void WriteJson(JsonWriter writer, object value, JsonSerializer s var jsonSerializer = new JsonSerializer { - TypeNameAssemblyFormat = System.Runtime.Serialization.Formatters.FormatterAssemblyStyle.Simple, + TypeNameAssemblyFormat = FormatterAssemblyStyle.Simple, TypeNameHandling = TypeNameHandling.Objects, Converters = { new JsonTypeConverter() } }; @@ -313,17 +322,16 @@ public override void WriteJson(JsonWriter writer, object value, JsonSerializer s private static object GetPropValue(object src, string propName) { - var pinf = src.GetType().GetProperty(propName); - if (pinf == null) + var prop = src.GetType().GetProperty(propName); + if (prop == null) { throw new ArgumentException($"There is no {propName} in {src.GetType().FullName}"); } - return pinf.GetValue(src, null); + return prop.GetValue(src, null); } - public override object ReadJson(JsonReader reader, Type objectType, object existingValue, - JsonSerializer serializer) + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { throw new NotImplementedException(); } From 63497efd9672f275034b9d3da51dfc19929af72f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pauli=20=C3=98ster=C3=B8?= Date: Fri, 24 Apr 2020 15:54:08 +0200 Subject: [PATCH 04/20] Change hardcoded usage of Microsoft.CSharp.CSharpProvider to instantiate it using CodeDomProvider.CreateProvider. This gives the flexibility to configure dynamic compilation via app/web.config, ie. using Roslyn for newer C# features. Implements feature #735 --- Composite/Composite.csproj | 1 + .../Core/Types/CSharpCodeProviderFactory.cs | 13 +++++++++++ .../Core/Types/CodeCompatibilityChecker.cs | 9 +++++--- Composite/Core/Types/CodeGenerationManager.cs | 22 +++++++++++++------ .../GeneratedTypes/GeneratedTypesHelper.cs | 2 +- .../Functions/Inline/InlineFunctionHelper.cs | 2 +- 6 files changed, 37 insertions(+), 12 deletions(-) create mode 100644 Composite/Core/Types/CSharpCodeProviderFactory.cs diff --git a/Composite/Composite.csproj b/Composite/Composite.csproj index aac5898d3f..59fb45371d 100644 --- a/Composite/Composite.csproj +++ b/Composite/Composite.csproj @@ -256,6 +256,7 @@ + diff --git a/Composite/Core/Types/CSharpCodeProviderFactory.cs b/Composite/Core/Types/CSharpCodeProviderFactory.cs new file mode 100644 index 0000000000..eaf5a324bc --- /dev/null +++ b/Composite/Core/Types/CSharpCodeProviderFactory.cs @@ -0,0 +1,13 @@ +using System.CodeDom.Compiler; +using Microsoft.CSharp; + +namespace Composite.Core.Types +{ + internal class CSharpCodeProviderFactory + { + public static CSharpCodeProvider CreateCompiler() + { + return (CSharpCodeProvider)CodeDomProvider.CreateProvider("c#"); + } + } +} diff --git a/Composite/Core/Types/CodeCompatibilityChecker.cs b/Composite/Core/Types/CodeCompatibilityChecker.cs index 19d1696f01..e8d1b61fd3 100644 --- a/Composite/Core/Types/CodeCompatibilityChecker.cs +++ b/Composite/Core/Types/CodeCompatibilityChecker.cs @@ -66,7 +66,7 @@ private static CompatibilityCheckResult CheckAgainsAppCode(DataTypeDescriptor da if (filesToCompile.Count == 0) return new CompatibilityCheckResult(); - var csCompiler = new CSharpCodeProvider(); + CSharpCodeProvider csCompiler = CSharpCodeProviderFactory.CreateCompiler(); List referencedAssemblies = new List(); var codeTypeDeclarations = new Dictionary>(); @@ -134,10 +134,13 @@ private static CompatibilityCheckResult CheckAgainsAppCode(DataTypeDescriptor da codeCompileUnit.Namespaces.Add(codeNamespace); } - var compiler = new CSharpCodeProvider(); + var compiler = CSharpCodeProviderFactory.CreateCompiler(); var compileResult = compiler.CompileAssemblyFromFile(compilerParameters, filesToCompile.ToArray()); - if (compileResult.Errors.Count == 0) return new CompatibilityCheckResult(); + if (!compileResult.Errors.HasErrors) + { + return new CompatibilityCheckResult(); + } // Checking for a missing assembly error, if it is present, that means that App_Code check isn't applicable due to circular reference foreach (CompilerError error in compileResult.Errors) diff --git a/Composite/Core/Types/CodeGenerationManager.cs b/Composite/Core/Types/CodeGenerationManager.cs index ce654f61fc..97edc76cf7 100644 --- a/Composite/Core/Types/CodeGenerationManager.cs +++ b/Composite/Core/Types/CodeGenerationManager.cs @@ -168,10 +168,10 @@ public static IEnumerable CompileRuntimeTempTypes(CodeGenerationBuilder co var codeCompileUnit = new CodeCompileUnit(); codeCompileUnit.Namespaces.AddRange(codeGenerationBuilder.Namespaces.ToArray()); - var compiler = new CSharpCodeProvider(); + var compiler = CSharpCodeProviderFactory.CreateCompiler(); var compileResult = compiler.CompileAssemblyFromDom(compilerParameters, codeCompileUnit); - if (compileResult.Errors.Count == 0) + if (!compileResult.Errors.HasErrors) { Assembly resultAssembly = compileResult.CompiledAssembly; @@ -212,7 +212,10 @@ public static IEnumerable CompileRuntimeTempTypes(CodeGenerationBuilder co sb.AppendLine("Failed building: " + codeGenerationBuilder.DebugLabel); foreach (CompilerError compilerError in compileResult.Errors) { - if (compilerError.IsWarning) continue; + if (compilerError.IsWarning) + { + continue; + } string entry = "Compile error: " + compilerError.ErrorNumber + "(" + compilerError.Line + ")" + ": " + compilerError.ErrorText.Replace("{", "{{").Replace("}", "}}"); @@ -338,11 +341,13 @@ private static void Compile(CodeGenerationBuilder builder) for (int i = 0; i < NumberOfCompileRetries; i++) { - var compiler = new CSharpCodeProvider(); + var compiler = CSharpCodeProviderFactory.CreateCompiler(); CompilerResults compileResult = compiler.CompileAssemblyFromDom(compilerParameters, codeCompileUnit); - - if (compileResult.Errors.Count == 0) return; + if (!compileResult.Errors.HasErrors) + { + return; + } if (i == NumberOfCompileRetries - 1) { @@ -359,7 +364,10 @@ private static void Compile(CodeGenerationBuilder builder) var sb = new StringBuilder(); foreach (CompilerError compilerError in compileResult.Errors) { - if (compilerError.IsWarning) continue; + if (compilerError.IsWarning) + { + continue; + } string entry = "Compile error: " + compilerError.ErrorNumber + "(" + compilerError.Line + ")" + ": " + compilerError.ErrorText.Replace("{", "{{").Replace("}", "}}"); diff --git a/Composite/Data/GeneratedTypes/GeneratedTypesHelper.cs b/Composite/Data/GeneratedTypes/GeneratedTypesHelper.cs index 0756b2a540..5e4d5815f8 100644 --- a/Composite/Data/GeneratedTypes/GeneratedTypesHelper.cs +++ b/Composite/Data/GeneratedTypes/GeneratedTypesHelper.cs @@ -726,7 +726,7 @@ private void CreateNewType() private bool IsCSharpValidIdentifier(string name) { - return new CSharpCodeProvider().IsValidIdentifier(name); + return CSharpCodeProviderFactory.CreateCompiler().IsValidIdentifier(name); } diff --git a/Composite/Functions/Inline/InlineFunctionHelper.cs b/Composite/Functions/Inline/InlineFunctionHelper.cs index 51f03eea9e..2e61134ace 100644 --- a/Composite/Functions/Inline/InlineFunctionHelper.cs +++ b/Composite/Functions/Inline/InlineFunctionHelper.cs @@ -89,7 +89,7 @@ public static MethodInfo Create(IInlineFunction function, string code = null, In compilerParameters.ReferencedAssemblies.Add(appCodeAssembly.Location); } - CSharpCodeProvider compiler = new CSharpCodeProvider(); + var compiler = CSharpCodeProviderFactory.CreateCompiler(); CompilerResults results = compiler.CompileAssemblyFromSource(compilerParameters, code); if (results.Errors.HasErrors) From b7b9eefebd1c4455f6bbb52c8011970b0b88273b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pauli=20=C3=98ster=C3=B8?= Date: Fri, 24 Apr 2020 16:05:35 +0200 Subject: [PATCH 05/20] Tidying and fixing Cachable -> Cacheable spelling mistakte --- .../AddNewCompositionTypeWorkflow.cs | 2 +- .../AddNewInterfaceTypeWorkflow.cs | 2 +- .../EditCompositionTypeWorkflow.cs | 4 +- .../EditInterfaceTypeWorkflow.cs | 4 +- .../AddInlineFunctionWorkflow.cs | 2 +- .../EditInlineFunctionWorkflow.cs | 2 +- .../Core/Types/CodeCompatibilityChecker.cs | 69 ++-- Composite/Core/Types/CodeGenerationManager.cs | 169 ++++------ .../GeneratedTypes/GeneratedTypesHelper.cs | 311 ++++++------------ .../Functions/Inline/InlineFunctionHelper.cs | 124 +++---- 10 files changed, 279 insertions(+), 410 deletions(-) diff --git a/Composite.Workflows/Plugins/Elements/ElementProviders/GeneratedDataTypesElementProvider/AddNewCompositionTypeWorkflow.cs b/Composite.Workflows/Plugins/Elements/ElementProviders/GeneratedDataTypesElementProvider/AddNewCompositionTypeWorkflow.cs index d9880ee27b..5a14407878 100644 --- a/Composite.Workflows/Plugins/Elements/ElementProviders/GeneratedDataTypesElementProvider/AddNewCompositionTypeWorkflow.cs +++ b/Composite.Workflows/Plugins/Elements/ElementProviders/GeneratedDataTypesElementProvider/AddNewCompositionTypeWorkflow.cs @@ -132,7 +132,7 @@ private void saveTypeCodeActivity_Save_ExecuteCode(object sender, EventArgs e) helper.SetLocalizedControlled(hasLocalization); } - helper.SetCachable(hasCaching); + helper.SetCacheable(hasCaching); helper.SetNewTypeFullName(typeName, typeNamespace); helper.SetNewTypeTitle(typeTitle); helper.SetNewFieldDescriptors(dataFieldDescriptors, null, labelFieldName); diff --git a/Composite.Workflows/Plugins/Elements/ElementProviders/GeneratedDataTypesElementProvider/AddNewInterfaceTypeWorkflow.cs b/Composite.Workflows/Plugins/Elements/ElementProviders/GeneratedDataTypesElementProvider/AddNewInterfaceTypeWorkflow.cs index 55eeae5aba..ab61d4eff7 100644 --- a/Composite.Workflows/Plugins/Elements/ElementProviders/GeneratedDataTypesElementProvider/AddNewInterfaceTypeWorkflow.cs +++ b/Composite.Workflows/Plugins/Elements/ElementProviders/GeneratedDataTypesElementProvider/AddNewInterfaceTypeWorkflow.cs @@ -166,7 +166,7 @@ private void codeActivity1_ExecuteCode(object sender, EventArgs e) if (helper.IsEditProcessControlledAllowed) { - helper.SetCachable(hasCaching); + helper.SetCacheable(hasCaching); helper.SetPublishControlled(hasPublishing); helper.SetLocalizedControlled(hasLocalization); helper.SetSearchable(isSearchable); diff --git a/Composite.Workflows/Plugins/Elements/ElementProviders/GeneratedDataTypesElementProvider/EditCompositionTypeWorkflow.cs b/Composite.Workflows/Plugins/Elements/ElementProviders/GeneratedDataTypesElementProvider/EditCompositionTypeWorkflow.cs index 78dba3175e..a9bfac6e93 100644 --- a/Composite.Workflows/Plugins/Elements/ElementProviders/GeneratedDataTypesElementProvider/EditCompositionTypeWorkflow.cs +++ b/Composite.Workflows/Plugins/Elements/ElementProviders/GeneratedDataTypesElementProvider/EditCompositionTypeWorkflow.cs @@ -62,7 +62,7 @@ private void initializeStateCodeActivity_Initialize_ExecuteCode(object sender, E {"TypeNamespace", dataTypeDescriptor.Namespace}, {"TypeTitle", dataTypeDescriptor.Title}, {"LabelFieldName", dataTypeDescriptor.LabelFieldName}, - {"HasCaching", helper.IsCachable}, + {"HasCaching", helper.IsCacheable}, {"HasPublishing", helper.IsPublishControlled}, {"DataFieldDescriptors", fieldDescriptors}, {"OldTypeName", dataTypeDescriptor.Name}, @@ -135,7 +135,7 @@ private void saveTypeCodeActivity_Save_ExecuteCode(object sender, EventArgs e) helper.SetNewTypeTitle(typeTitle); // TODO: fix helper.SetNewFieldDescriptors(dataFieldDescriptors, null, labelFieldName); - helper.SetCachable(hasCaching); + helper.SetCacheable(hasCaching); if (helper.IsEditProcessControlledAllowed) { diff --git a/Composite.Workflows/Plugins/Elements/ElementProviders/GeneratedDataTypesElementProvider/EditInterfaceTypeWorkflow.cs b/Composite.Workflows/Plugins/Elements/ElementProviders/GeneratedDataTypesElementProvider/EditInterfaceTypeWorkflow.cs index 79d99eb3f2..5cef7bb926 100644 --- a/Composite.Workflows/Plugins/Elements/ElementProviders/GeneratedDataTypesElementProvider/EditInterfaceTypeWorkflow.cs +++ b/Composite.Workflows/Plugins/Elements/ElementProviders/GeneratedDataTypesElementProvider/EditInterfaceTypeWorkflow.cs @@ -82,7 +82,7 @@ private void initialStateCodeActivity_ExecuteCode(object sender, EventArgs e) {BindingNames.KeyFieldName, dataTypeDescriptor.KeyPropertyNames.Single() }, {BindingNames.LabelFieldName, dataTypeDescriptor.LabelFieldName}, {BindingNames.InternalUrlPrefix, dataTypeDescriptor.InternalUrlPrefix}, - {BindingNames.HasCaching, helper.IsCachable}, + {BindingNames.HasCaching, helper.IsCacheable}, {BindingNames.HasPublishing, helper.IsPublishControlled}, {BindingNames.IsSearchable, helper.IsSearchable}, {BindingNames.DataFieldDescriptors, fieldDescriptors}, @@ -164,7 +164,7 @@ private void finalizeStateCodeActivity_ExecuteCode(object sender, EventArgs e) if (helper.IsEditProcessControlledAllowed) { - helper.SetCachable(hasCaching); + helper.SetCacheable(hasCaching); helper.SetSearchable(isSearchable); helper.SetPublishControlled(hasPublishing); helper.SetLocalizedControlled(hasLocalization); diff --git a/Composite.Workflows/Plugins/Elements/ElementProviders/MethodBasedFunctionProviderElementProvider/AddInlineFunctionWorkflow.cs b/Composite.Workflows/Plugins/Elements/ElementProviders/MethodBasedFunctionProviderElementProvider/AddInlineFunctionWorkflow.cs index 924c1a5d03..bffdc0842f 100644 --- a/Composite.Workflows/Plugins/Elements/ElementProviders/MethodBasedFunctionProviderElementProvider/AddInlineFunctionWorkflow.cs +++ b/Composite.Workflows/Plugins/Elements/ElementProviders/MethodBasedFunctionProviderElementProvider/AddInlineFunctionWorkflow.cs @@ -116,7 +116,7 @@ private void finalizeCodeActivity_Finalize_ExecuteCode(object sender, EventArgs DataFacade.AddNew(reference); } - function.SetFunctinoCode(code); + function.SetFunctionCode(code); function = DataFacade.AddNew(function); diff --git a/Composite.Workflows/Plugins/Elements/ElementProviders/MethodBasedFunctionProviderElementProvider/EditInlineFunctionWorkflow.cs b/Composite.Workflows/Plugins/Elements/ElementProviders/MethodBasedFunctionProviderElementProvider/EditInlineFunctionWorkflow.cs index 2b8c2d13b7..9e26d14b2a 100644 --- a/Composite.Workflows/Plugins/Elements/ElementProviders/MethodBasedFunctionProviderElementProvider/EditInlineFunctionWorkflow.cs +++ b/Composite.Workflows/Plugins/Elements/ElementProviders/MethodBasedFunctionProviderElementProvider/EditInlineFunctionWorkflow.cs @@ -162,7 +162,7 @@ private void saveCodeActivity_Save_ExecuteCode(object sender, EventArgs e) ManagedParameterManager.Save(function.Id, parameters); DataFacade.Update(function); - InlineFunctionHelper.SetFunctinoCode(function, code); + function.SetFunctionCode(code); transactionScope.Complete(); } diff --git a/Composite/Core/Types/CodeCompatibilityChecker.cs b/Composite/Core/Types/CodeCompatibilityChecker.cs index e8d1b61fd3..b4fe57ac73 100644 --- a/Composite/Core/Types/CodeCompatibilityChecker.cs +++ b/Composite/Core/Types/CodeCompatibilityChecker.cs @@ -13,8 +13,6 @@ using Composite.Data; using Composite.Data.DynamicTypes; using Composite.Data.GeneratedTypes; -using Microsoft.CSharp; - namespace Composite.Core.Types { @@ -30,11 +28,9 @@ internal static class CodeCompatibilityChecker /// public static CompatibilityCheckResult CheckCompatibilityWithAppCodeFolder(DataTypeDescriptor dataTypeDescriptorToTest) { - return CheckAgainsAppCode(dataTypeDescriptorToTest, true); + return CheckAgainstAppCode(dataTypeDescriptorToTest, true); } - - /// /// This method will check if any code in en App_Code folder depends on the given data interface. /// @@ -42,11 +38,9 @@ public static CompatibilityCheckResult CheckCompatibilityWithAppCodeFolder(DataT /// public static CompatibilityCheckResult CheckIfAppCodeDependsOnInterface(DataTypeDescriptor dataTypeDescriptorToTest) { - return CheckAgainsAppCode(dataTypeDescriptorToTest, false); + return CheckAgainstAppCode(dataTypeDescriptorToTest, false); } - - /// /// This method checks to see if any change in the given data type descriptor will make code /// in App_Code fail and hence the site will fail. @@ -60,44 +54,57 @@ public static CompatibilityCheckResult CheckIfAppCodeDependsOnInterface(DataType [SuppressMessage("Composite.IO", "Composite.DotNotUseStreamWriterClass:DotNotUseStreamWriterClass", Justification = "File api is used for creating temporary files")] [SuppressMessage("Composite.IO", "Composite.DoNotUseFileStreamClass:DoNotUseFileStreamClass", Justification = "File api is used for creating temporary files")] [SuppressMessage("Composite.IO", "Composite.DoNotUseFileClass:DoNotUseFileClass", Justification = "File api is used for creating temporary files")] - private static CompatibilityCheckResult CheckAgainsAppCode(DataTypeDescriptor dataTypeDescriptorToTest, bool includeDataTypeDescriptor) + private static CompatibilityCheckResult CheckAgainstAppCode(DataTypeDescriptor dataTypeDescriptorToTest, bool includeDataTypeDescriptor) { - List filesToCompile = GetAppCodeFiles().ToList(); - - if (filesToCompile.Count == 0) return new CompatibilityCheckResult(); + var filesToCompile = GetAppCodeFiles().ToList(); + if (filesToCompile.Count == 0) + { + return new CompatibilityCheckResult(); + } - CSharpCodeProvider csCompiler = CSharpCodeProviderFactory.CreateCompiler(); + var csCompiler = CSharpCodeProviderFactory.CreateCompiler(); - List referencedAssemblies = new List(); + var referencedAssemblies = new List(); var codeTypeDeclarations = new Dictionary>(); foreach (var dataTypeDescriptor in DataMetaDataFacade.GeneratedTypeDataTypeDescriptors) { - if (!includeDataTypeDescriptor && dataTypeDescriptor.DataTypeId == dataTypeDescriptorToTest.DataTypeId) continue; + if (!includeDataTypeDescriptor && dataTypeDescriptor.DataTypeId == dataTypeDescriptorToTest.DataTypeId) + { + continue; + } + + var dataTypeDescriptorToUse = dataTypeDescriptor; - DataTypeDescriptor dataTypeDescriptorToUse = dataTypeDescriptor; - if (includeDataTypeDescriptor && dataTypeDescriptor.DataTypeId == dataTypeDescriptorToTest.DataTypeId) dataTypeDescriptorToUse = dataTypeDescriptorToTest; + if (includeDataTypeDescriptor && dataTypeDescriptor.DataTypeId == dataTypeDescriptorToTest.DataTypeId) + { + dataTypeDescriptorToUse = dataTypeDescriptorToTest; + } referencedAssemblies.AddRange(InterfaceCodeGenerator.GetReferencedAssemblies(dataTypeDescriptorToUse)); - CodeTypeDeclaration codeTypeDeclaration = InterfaceCodeGenerator.CreateCodeTypeDeclaration(dataTypeDescriptorToUse); - List declarations; - if (!codeTypeDeclarations.TryGetValue(dataTypeDescriptorToUse.Namespace, out declarations)) + var codeTypeDeclaration = InterfaceCodeGenerator.CreateCodeTypeDeclaration(dataTypeDescriptorToUse); + if (!codeTypeDeclarations.TryGetValue(dataTypeDescriptorToUse.Namespace, out var declarations)) { declarations = new List(); + codeTypeDeclarations.Add(dataTypeDescriptorToUse.Namespace, declarations); } + declarations.Add(codeTypeDeclaration); - string tempFilePath = GetTempFileName(dataTypeDescriptorToUse); + var tempFilePath = GetTempFileName(dataTypeDescriptorToUse); + filesToCompile.Add(tempFilePath); - using (FileStream file = File.Create(tempFilePath)) + using (var file = File.Create(tempFilePath)) { using (var sw = new StreamWriter(file)) { var codeNamespace = new CodeNamespace(dataTypeDescriptorToUse.Namespace); + codeNamespace.Types.Add(codeTypeDeclaration); + csCompiler.GenerateCodeFromNamespace(codeNamespace, sw, new CodeGeneratorOptions()); } @@ -111,7 +118,6 @@ private static CompatibilityCheckResult CheckAgainsAppCode(DataTypeDescriptor da filesToCompile.Sort(); - var compilerParameters = new CompilerParameters { GenerateExecutable = false, @@ -120,16 +126,17 @@ private static CompatibilityCheckResult CheckAgainsAppCode(DataTypeDescriptor da compilerParameters.ReferencedAssemblies.AddRangeIfNotContained(referencedAssemblies.Select(f => f.Location).ToArray()); compilerParameters.ReferencedAssemblies.AddRangeIfNotContained(CodeGenerationManager.CompiledAssemblies.Select(f => f.Location).ToArray()); + compilerParameters.AddLoadedAssemblies(false); compilerParameters.AddAssemblyLocationsFromBin(); compilerParameters.AddCommonAssemblies(); compilerParameters.RemoveGeneratedAssemblies(); - var codeCompileUnit = new CodeCompileUnit(); foreach (var kvp in codeTypeDeclarations) { var codeNamespace = new CodeNamespace(kvp.Key); + codeNamespace.Types.AddRange(kvp.Value.ToArray()); codeCompileUnit.Namespaces.Add(codeNamespace); } @@ -157,9 +164,9 @@ private static CompatibilityCheckResult CheckAgainsAppCode(DataTypeDescriptor da private static string GetTempFileName(DataTypeDescriptor typeDescriptor) { - string folderPath = PathUtil.Resolve(GlobalSettingsFacade.GeneratedAssembliesDirectory); + var folderPath = PathUtil.Resolve(GlobalSettingsFacade.GeneratedAssembliesDirectory); - string filePath = Path.Combine(folderPath, typeDescriptor.GetFullInterfaceName() + ".cs"); + var filePath = Path.Combine(folderPath, typeDescriptor.GetFullInterfaceName() + ".cs"); if (filePath.Length > 255) { filePath = Path.Combine(folderPath, typeDescriptor.DataTypeId + ".cs"); @@ -169,11 +176,13 @@ private static string GetTempFileName(DataTypeDescriptor typeDescriptor) } [SuppressMessage("Composite.IO", "Composite.DoNotUseDirectoryClass:DoNotUseDirectoryClass")] - private static string[] GetAppCodeFiles() + private static IEnumerable GetAppCodeFiles() { - string appCodeFolderPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, GlobalSettingsFacade.AppCodeDirectory); - - if (!Directory.Exists(appCodeFolderPath)) return new string[0]; + var appCodeFolderPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, GlobalSettingsFacade.AppCodeDirectory); + if (!Directory.Exists(appCodeFolderPath)) + { + return new string[0]; + } return Directory.GetFiles(appCodeFolderPath, "*.cs", SearchOption.AllDirectories); } diff --git a/Composite/Core/Types/CodeGenerationManager.cs b/Composite/Core/Types/CodeGenerationManager.cs index 97edc76cf7..ab852fe9d4 100644 --- a/Composite/Core/Types/CodeGenerationManager.cs +++ b/Composite/Core/Types/CodeGenerationManager.cs @@ -6,6 +6,7 @@ using System.CodeDom.Compiler; using System.Collections.Generic; using System.Collections.Specialized; +using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; @@ -17,8 +18,6 @@ using Composite.Core.PackageSystem; using Composite.Data.Foundation.CodeGeneration; using Composite.Data.GeneratedTypes; -using Microsoft.CSharp; - namespace Composite.Core.Types { @@ -34,7 +33,7 @@ public static class CodeGenerationManager private static bool _compositeGeneratedCompiled = true; private static List _dynamicallyAddedCodeProviders = new List(); private static readonly List _compiledAssemblies = new List(); - private static readonly Dictionary _compiledTypesByFullName = new Dictionary(); + private static readonly Dictionary _compiledTypesByFullName = new Dictionary(); /// /// If set to true, /Bin/Composite.Generated.dll won't be overwritten on shutdown @@ -63,12 +62,10 @@ static CodeGenerationManager() } GlobalEventSystemFacade.SubscribeToFlushEvent(args => Flush()); - GlobalEventSystemFacade.SubscribeToShutDownEvent(args => ClearOldTempFiles()); } - /// /// Validates that the current Composite.Generated.dll is not compiled after the given /// time. If it is compiled after the given time. Any attempts to recompile Composite.Generated.dll @@ -77,29 +74,37 @@ static CodeGenerationManager() /// internal static void ValidateCompositeGenerate(DateTime time) { - if (SuppressGeneration) return; - - string filePath = Path.Combine(PathUtil.BaseDirectory, "Bin", "Composite.Generated.dll"); - - if (!C1File.Exists(filePath)) return; + if (SuppressGeneration) + { + return; + } - DateTime lastWrite = C1File.GetLastWriteTime(filePath); + var filePath = Path.Combine(PathUtil.BaseDirectory, "Bin", "Composite.Generated.dll"); + if (!C1File.Exists(filePath)) + { + return; + } - if (lastWrite <= time) return; + var lastWrite = C1File.GetLastWriteTime(filePath); + if (lastWrite <= time) + { + return; + } _compositeGeneratedCompiled = true; Log.LogVerbose(LogTitle, $"Assembly in this application domain is newer than this application domain ({AppDomain.CurrentDomain.Id})"); } - - /// /// This method will recompile Composite.Generated.dll and drop it into bin. /// /// public static void GenerateCompositeGeneratedAssembly(bool forceGeneration = false) { - if (SuppressGeneration) return; + if (SuppressGeneration) + { + return; + } if (forceGeneration || !_compositeGeneratedCompiled) { @@ -109,21 +114,21 @@ public static void GenerateCompositeGeneratedAssembly(bool forceGeneration = fal { Log.LogVerbose(LogTitle, $"Compiling new assembly in this application domain ({AppDomain.CurrentDomain.Id})"); - int t1 = Environment.TickCount; + var t1 = Environment.TickCount; var builder = new CodeGenerationBuilder("Composite.Generated.dll"); PopulateBuilder(builder); - int t2 = Environment.TickCount; + var t2 = Environment.TickCount; Compile(builder); - int t3 = Environment.TickCount; + var t3 = Environment.TickCount; - int numberOfTypes = builder.Namespaces.SelectMany(f => f.Types.OfType()).Count(); + var numberOfTypes = builder.Namespaces.SelectMany(f => f.Types.OfType()).Count(); Log.LogVerbose(LogTitle, "Number of types build: " + numberOfTypes + - "\nBuilding code dom: " + (t2 - t1) + "ms" + + "\nBuilding code dom: " + (t2 - t1) + "ms" + "\nCompiling code dom: " + (t3 - t2) + "ms" + "\nTotal compilation: " + (t3 - t1) + "ms"); @@ -137,8 +142,6 @@ public static void GenerateCompositeGeneratedAssembly(bool forceGeneration = fal Log.LogVerbose(LogTitle, "New assembly already compiled by this application domain ({0})", AppDomain.CurrentDomain.Id); } - - /// /// This method will compile the type defined in /// and return the result types. These types exists in a temp assembly, that will be @@ -149,7 +152,7 @@ public static void GenerateCompositeGeneratedAssembly(bool forceGeneration = fal /// public static IEnumerable CompileRuntimeTempTypes(CodeGenerationBuilder codeGenerationBuilder, bool verbose = true) { - int t1 = Environment.TickCount; + var t1 = Environment.TickCount; _compositeGeneratedCompiled = false; // When compiling a new type, Composite.Generated.dll should always be recompiled @@ -164,8 +167,8 @@ public static IEnumerable CompileRuntimeTempTypes(CodeGenerationBuilder co compilerParameters.ReferencedAssemblies.AddRangeIfNotContained(_compiledAssemblies.Select(f => f.Location).ToArray()); compilerParameters.AddAssemblyLocationsFromBin(); - var codeCompileUnit = new CodeCompileUnit(); + codeCompileUnit.Namespaces.AddRange(codeGenerationBuilder.Namespaces.ToArray()); var compiler = CSharpCodeProviderFactory.CreateCompiler(); @@ -173,18 +176,18 @@ public static IEnumerable CompileRuntimeTempTypes(CodeGenerationBuilder co if (!compileResult.Errors.HasErrors) { - Assembly resultAssembly = compileResult.CompiledAssembly; + var resultAssembly = compileResult.CompiledAssembly; AddCompiledAssembly(resultAssembly); - Type[] resultTypes = resultAssembly.GetTypes(); + var resultTypes = resultAssembly.GetTypes(); - int t2 = Environment.TickCount; + var t2 = Environment.TickCount; Log.LogVerbose(LogTitle, $"Compile '{codeGenerationBuilder.DebugLabel}' in {t2 - t1}ms"); Log.LogVerbose(LogTitle, $"Types from : {compilerParameters.OutputAssembly}"); - foreach (Type resultType in resultTypes) + foreach (var resultType in resultTypes) { _compiledTypesByFullName[resultType.FullName] = resultType; } @@ -192,17 +195,7 @@ public static IEnumerable CompileRuntimeTempTypes(CodeGenerationBuilder co return resultTypes; } - -#if OUTPUT_SOURCE_CODE_ON_ERROR - using (FileStream file = File.Create(Path.Combine(PathUtil.BaseDirectory, "output.cs"))) - { - using (var sw = new StreamWriter(file)) - { - compiler.GenerateCodeFromCompileUnit(codeCompileUnit, sw, new CodeGeneratorOptions()); - } - } -#endif - + OutputSourceCodeOnError(compiler, codeCompileUnit); var failedAssemblyLoads = LoadAssembliesToMemory(compilerParameters.ReferencedAssemblies); @@ -217,7 +210,7 @@ public static IEnumerable CompileRuntimeTempTypes(CodeGenerationBuilder co continue; } - string entry = "Compile error: " + compilerError.ErrorNumber + "(" + compilerError.Line + ")" + ": " + compilerError.ErrorText.Replace("{", "{{").Replace("}", "}}"); + var entry = "Compile error: " + compilerError.ErrorNumber + "(" + compilerError.Line + ")" + ": " + compilerError.ErrorText.Replace("{", "{{").Replace("}", "}}"); if (verbose) { @@ -230,11 +223,10 @@ public static IEnumerable CompileRuntimeTempTypes(CodeGenerationBuilder co throw new InvalidOperationException(sb.ToString()); } - - private static ICollection> LoadAssembliesToMemory(StringCollection assemblyLocations) + private static IEnumerable> LoadAssembliesToMemory(StringCollection assemblyLocations) { var failedAssemblyLoads = new List>(); - foreach (string assemblyLocation in assemblyLocations) + foreach (var assemblyLocation in assemblyLocations) { if (PackageAssemblyHandler.TryGetAlreadyLoadedAssembly(assemblyLocation) != null) { @@ -248,7 +240,7 @@ private static ICollection> LoadAssembliesToMemory(Strin } catch (Exception ex) { - Exception exceptionToLog = ex; + var exceptionToLog = ex; var loadException = ex as ReflectionTypeLoadException; if (loadException?.LoaderExceptions != null && loadException.LoaderExceptions.Any()) @@ -263,7 +255,6 @@ private static ICollection> LoadAssembliesToMemory(Strin return failedAssemblyLoads; } - /// /// This method returns true if the given type is /// compiled at runetime. Otherwise false. @@ -275,9 +266,6 @@ public static bool IsCompiledAtRuntime(Type type) return type.Assembly.Location.StartsWith(PathUtil.Resolve(GlobalSettingsFacade.GeneratedAssembliesDirectory), StringComparison.InvariantCultureIgnoreCase); } - - - /// /// This method returns true if the types given by needs a recompile because /// they either is null or the type given by has changed and there for @@ -288,18 +276,22 @@ public static bool IsCompiledAtRuntime(Type type) /// Returns true if the types given by needs a recompile. public static bool IsRecompileNeeded(Type dependableType, IEnumerable dependingTypes) { - foreach (Type dependingType in dependingTypes) + foreach (var dependingType in dependingTypes) { - if (dependingType == null) return true; + if (dependingType == null) + { + return true; + } - if (IsCompiledAtRuntime(dependableType) && !IsCompiledAtRuntime(dependingType)) return true; + if (IsCompiledAtRuntime(dependableType) && !IsCompiledAtRuntime(dependingType)) + { + return true; + } } return false; } - - /// /// Use this method to add a implementation /// that will be used when (and only) generating the final Composite.Generated.dll. @@ -310,18 +302,15 @@ public static void AddAssemblyCodeProvider(ICodeProvider codeProvider) _dynamicallyAddedCodeProviders.Add(codeProvider); } - /// /// Gets the compiled types. /// /// public static Type GetCompiledType(string fullName) { - Type type; - return _compiledTypesByFullName.TryGetValue(fullName, out type) ? type : null; + return _compiledTypesByFullName.TryGetValue(fullName, out var type) ? type : null; } - private static void Compile(CodeGenerationBuilder builder) { var compilerParameters = new CompilerParameters @@ -336,10 +325,10 @@ private static void Compile(CodeGenerationBuilder builder) compilerParameters.AddAssemblyLocationsFromBin(); var codeCompileUnit = new CodeCompileUnit(); - codeCompileUnit.Namespaces.AddRange(builder.Namespaces.ToArray()); + codeCompileUnit.Namespaces.AddRange(builder.Namespaces.ToArray()); - for (int i = 0; i < NumberOfCompileRetries; i++) + for (var i = 0; i < NumberOfCompileRetries; i++) { var compiler = CSharpCodeProviderFactory.CreateCompiler(); CompilerResults compileResult = compiler.CompileAssemblyFromDom(compilerParameters, codeCompileUnit); @@ -351,15 +340,7 @@ private static void Compile(CodeGenerationBuilder builder) if (i == NumberOfCompileRetries - 1) { -#if OUTPUT_SOURCE_CODE_ON_ERROR - using (FileStream file = File.Create(Path.Combine(PathUtil.BaseDirectory, "output.cs"))) - { - using (var sw = new StreamWriter(file)) - { - compiler.GenerateCodeFromCompileUnit(codeCompileUnit, sw, new CodeGeneratorOptions()); - } - } -#endif + OutputSourceCodeOnError(compiler, codeCompileUnit); var sb = new StringBuilder(); foreach (CompilerError compilerError in compileResult.Errors) @@ -369,7 +350,7 @@ private static void Compile(CodeGenerationBuilder builder) continue; } - string entry = "Compile error: " + compilerError.ErrorNumber + "(" + compilerError.Line + ")" + ": " + compilerError.ErrorText.Replace("{", "{{").Replace("}", "}}"); + var entry = "Compile error: " + compilerError.ErrorNumber + "(" + compilerError.Line + ")" + ": " + compilerError.ErrorText.Replace("{", "{{").Replace("}", "}}"); Log.LogError(LogTitle, entry); @@ -381,53 +362,35 @@ private static void Compile(CodeGenerationBuilder builder) } } - - /// /// Returns all currently temp compiled assemblies. /// internal static IEnumerable CompiledAssemblies => _compiledAssemblies; - /// /// internal static string TempAssemblyFolderPath => PathUtil.Resolve(GlobalSettingsFacade.GeneratedAssembliesDirectory); - /// /// - internal static string BinFolder - { - get - { - return RuntimeInformation.IsUnittest - ? PathUtil.BaseDirectory - : Path.Combine(PathUtil.BaseDirectory, "Bin"); - } - } - - + internal static string BinFolder => RuntimeInformation.IsUnittest ? PathUtil.BaseDirectory : Path.Combine(PathUtil.BaseDirectory, "Bin"); /// /// internal static string CompositeGeneratedFileName => "Composite.Generated.dll"; - /// /// internal static string CompositeGeneratedAssemblyPath => Path.Combine(BinFolder, "Composite.Generated.dll"); - private static void PopulateBuilder(CodeGenerationBuilder builder) { - foreach (ICodeProvider provider in CodeProviders) + foreach (var provider in CodeProviders) { provider.GetCodeToCompile(builder); } } - - private static IEnumerable CodeProviders { get @@ -436,30 +399,28 @@ private static IEnumerable CodeProviders yield return new EmptyDataClassCodeProvider(); yield return new DataWrapperClassCodeProvider(); - foreach (ICodeProvider codeProvider in _dynamicallyAddedCodeProviders) + foreach (var codeProvider in _dynamicallyAddedCodeProviders) { yield return codeProvider; } } } - - private static void AddCompiledAssembly(Assembly newAssembly) { - Type newType = newAssembly.GetTypes().First(); + var newType = newAssembly.GetTypes().First(); var assembliesToRemove = new List(); - foreach (Assembly assembly in _compiledAssemblies) + foreach (var assembly in _compiledAssemblies) { - Type type = assembly.GetTypes().SingleOrDefault(f => f.FullName == newType.FullName); + var type = assembly.GetTypes().SingleOrDefault(f => f.FullName == newType.FullName); if (type != null) { assembliesToRemove.Add(assembly); } } - foreach (Assembly assemblyToRemove in assembliesToRemove) + foreach (var assemblyToRemove in assembliesToRemove) { _compiledAssemblies.Remove(assemblyToRemove); } @@ -467,8 +428,6 @@ private static void AddCompiledAssembly(Assembly newAssembly) _compiledAssemblies.Add(newAssembly); } - - private static void Flush() { _dynamicallyAddedCodeProviders = new List(); @@ -476,7 +435,7 @@ private static void Flush() private static void ClearOldTempFiles() { - DateTime yesterday = DateTime.Now.AddDays(-1); + var yesterday = DateTime.Now.AddDays(-1); var oldFiles = C1Directory.GetFiles(TempAssemblyFolderPath, "*.*").Where(filePath => C1File.GetCreationTime(filePath) < yesterday).ToArray(); foreach (var file in oldFiles) @@ -491,5 +450,17 @@ private static void ClearOldTempFiles() } } } + + [Conditional("OUTPUT_SOURCE_CODE_ON_ERROR")] + private static void OutputSourceCodeOnError(CodeDomProvider compiler, CodeCompileUnit codeCompileUnit) + { + using (var file = File.Create(Path.Combine(PathUtil.BaseDirectory, "output.cs"))) + { + using (var sw = new StreamWriter(file)) + { + compiler.GenerateCodeFromCompileUnit(codeCompileUnit, sw, new CodeGeneratorOptions()); + } + } + } } } diff --git a/Composite/Data/GeneratedTypes/GeneratedTypesHelper.cs b/Composite/Data/GeneratedTypes/GeneratedTypesHelper.cs index 5e4d5815f8..ebd86b1799 100644 --- a/Composite/Data/GeneratedTypes/GeneratedTypesHelper.cs +++ b/Composite/Data/GeneratedTypes/GeneratedTypesHelper.cs @@ -11,7 +11,6 @@ using Composite.Data.ProcessControlled; using Composite.Data.Types; using Composite.Functions; -using Microsoft.CSharp; using Texts = Composite.Core.ResourceSystem.LocalizationFiles.Composite_GeneratedTypes; @@ -38,7 +37,7 @@ public enum KeyFieldType private static readonly string[] ReservedNamespaces = { "System", "Composite.Data.GeneratedTypes", "GeneratedTypes" }; - private static readonly string CompositeNamespace = "Composite"; + private const string CompositeNamespace = "Composite"; private Type _associatedType; private readonly Type _oldType; @@ -48,7 +47,7 @@ public enum KeyFieldType private string _newTypeName; private string _newTypeNamespace; private string _newTypeTitle; - private bool _cachable; + private bool _cacheable; private bool _searchable; private bool _publishControlled; private bool _localizedControlled; @@ -65,19 +64,14 @@ public enum KeyFieldType private bool _typeCreated; - private static readonly string IdFieldName = "Id"; - private static readonly string PageReferenceFieldName = "PageId"; - private static readonly string CompositionDescriptionFieldName = "FieldName"; + private const string IdFieldName = "Id"; + private const string PageReferenceFieldName = "PageId"; + private const string CompositionDescriptionFieldName = "FieldName"; //private KeyFieldType _keyFieldType = KeyFieldType.Guid; - /// - public GeneratedTypesHelper() - { - } - - + public GeneratedTypesHelper() { } /// public GeneratedTypesHelper(Type oldType) @@ -90,8 +84,6 @@ public GeneratedTypesHelper(Type oldType) Initialize(); } - - /// public GeneratedTypesHelper(DataTypeDescriptor oldDataTypeDescriptor) { @@ -103,16 +95,8 @@ public GeneratedTypesHelper(DataTypeDescriptor oldDataTypeDescriptor) Initialize(); } - - /// - public bool AllowForeignKeyEditing - { - get; - set; - } - - + public bool AllowForeignKeyEditing { get; set; } /// [Obsolete("Use EditableOwnDataFieldDescriptors which does not return inherited fields", true)] @@ -150,16 +134,17 @@ public IEnumerable NotEditableDataFieldDescriptorNames Verify.IsNotNull(_oldDataTypeDescriptor, "No old data type specified"); return from field in _oldDataTypeDescriptor.Fields - where !IsDataFieldBindable(_oldDataTypeDescriptor, field) - || _oldDataTypeDescriptor.KeyPropertyNames.FirstOrDefault() == field.Name + where !IsDataFieldBindable(_oldDataTypeDescriptor, field) || _oldDataTypeDescriptor.KeyPropertyNames.FirstOrDefault() == field.Name select field.Name; } } - + /// + [Obsolete("Use IsCacheable")] + public bool IsCachable => IsCacheable; /// - public bool IsCachable + public bool IsCacheable { get { @@ -169,7 +154,6 @@ public bool IsCachable } } - /// public bool IsSearchable { @@ -181,7 +165,6 @@ public bool IsSearchable } } - /// public bool IsPublishControlled { @@ -193,8 +176,6 @@ public bool IsPublishControlled } } - - /// public bool IsLocalizedControlled { @@ -206,20 +187,10 @@ public bool IsLocalizedControlled } } - - /// /// Returns true if the date type is a page meta data type. /// - public bool IsEditProcessControlledAllowed - { - get - { - return _pageMetaDataDescriptionForeignKeyDataFieldDescriptor == null; - } - } - - + public bool IsEditProcessControlledAllowed => _pageMetaDataDescriptionForeignKeyDataFieldDescriptor == null; /// public bool ValidateNewTypeName(string typeName, out string message) @@ -229,8 +200,6 @@ public bool ValidateNewTypeName(string typeName, out string message) return NameValidation.TryValidateName(typeName, out message); } - - /// public bool ValidateNewTypeNamespace(string typeNamespace, out string message) { @@ -239,8 +208,6 @@ public bool ValidateNewTypeNamespace(string typeNamespace, out string message) return NameValidation.TryValidateNamespace(typeNamespace, out message); } - - /// public bool ValidateNewTypeFullName(string typeName, string typeNamespace, out string message) { @@ -255,8 +222,6 @@ public bool ValidateNewTypeFullName(string typeName, string typeNamespace, out s return false; } - - if (_oldDataTypeDescriptor != null) { if (_oldDataTypeDescriptor.Name == typeName && @@ -265,7 +230,7 @@ public bool ValidateNewTypeFullName(string typeName, string typeNamespace, out s return true; } - Type interfaceType = _oldDataTypeDescriptor.GetInterfaceType(); + var interfaceType = _oldDataTypeDescriptor.GetInterfaceType(); if (interfaceType.GetRefereeTypes().Count > 0) { @@ -274,27 +239,28 @@ public bool ValidateNewTypeFullName(string typeName, string typeNamespace, out s } } - string typeFullname = StringExtensionMethods.CreateNamespace(typeNamespace, typeName, '.'); - foreach (DataTypeDescriptor dtd in DataMetaDataFacade.GeneratedTypeDataTypeDescriptors) + var typeFullname = StringExtensionMethods.CreateNamespace(typeNamespace, typeName, '.'); + foreach (var dtd in DataMetaDataFacade.GeneratedTypeDataTypeDescriptors) { - string fullname = StringExtensionMethods.CreateNamespace(dtd.Namespace, dtd.Name, '.'); - + var fullname = StringExtensionMethods.CreateNamespace(dtd.Namespace, dtd.Name, '.'); if (typeFullname == fullname) { message = Texts.TypesNameClash; + return false; } } - - string[] partNames = typeFullname.Split('.'); + var partNames = typeFullname.Split('.'); var sb = new StringBuilder(partNames[0]); - for (int i = 1; i < partNames.Length; i++) + + for (var i = 1; i < partNames.Length; i++) { - bool exists = TypeManager.HasTypeWithName(sb.ToString()); + var exists = TypeManager.HasTypeWithName(sb.ToString()); if (exists) { message = Texts.NameSpaceIsTypeTypeName(sb.ToString()); + return false; } @@ -305,38 +271,39 @@ public bool ValidateNewTypeFullName(string typeName, string typeNamespace, out s return true; } - - /// public bool ValidateByCompile(out string errorMessage) { var dataTypeDescriptor = _oldDataTypeDescriptor == null ? CreateNewDataTypeDescriptor() : CreateUpdatedDataTypeDescriptor(); - string classFullName = (dataTypeDescriptor.Namespace + "." + dataTypeDescriptor.Name).Replace(" ", string.Empty); - string classFullNameWithDot = classFullName + "."; + var classFullName = (dataTypeDescriptor.Namespace + "." + dataTypeDescriptor.Name).Replace(" ", string.Empty); + var classFullNameWithDot = classFullName + "."; foreach (var reservedNamespace in ReservedNamespaces) { if (classFullNameWithDot.StartsWith(reservedNamespace + ".", StringComparison.InvariantCultureIgnoreCase)) { errorMessage = Texts.NamespaceIsReserved; + return false; } } - foreach (string namePart in classFullName.Split(new[] { '.' }, StringSplitOptions.RemoveEmptyEntries)) + foreach (var namePart in classFullName.Split(new[] { '.' }, StringSplitOptions.RemoveEmptyEntries)) { if (!IsCSharpValidIdentifier(namePart)) { errorMessage = Texts.TypeNameIsInvalidIdentifier(classFullName); + return false; } } - foreach (DataFieldDescriptor dataField in dataTypeDescriptor.Fields) + foreach (var dataField in dataTypeDescriptor.Fields) { if (!IsCSharpValidIdentifier(dataField.Name)) { errorMessage = Texts.FieldNameCannotBeUsed(dataField.Name); + return false; } } @@ -346,34 +313,32 @@ public bool ValidateByCompile(out string errorMessage) { foreach (var type in typeof(IData).Assembly.GetTypes()) { - string typeNameWithDot = type.FullName + "."; + var typeNameWithDot = type.FullName + "."; - if (classFullNameWithDot.StartsWith(typeNameWithDot, StringComparison.InvariantCultureIgnoreCase) - || typeNameWithDot.StartsWith(classFullNameWithDot, StringComparison.InvariantCultureIgnoreCase)) + if (classFullNameWithDot.StartsWith(typeNameWithDot, StringComparison.InvariantCultureIgnoreCase) || typeNameWithDot.StartsWith(classFullNameWithDot, StringComparison.InvariantCultureIgnoreCase)) { errorMessage = Texts.CompileErrorWhileAddingType; + return false; } } } - - CompatibilityCheckResult compatibilityCheckResult = CodeCompatibilityChecker.CheckCompatibilityWithAppCodeFolder(dataTypeDescriptor); - + var compatibilityCheckResult = CodeCompatibilityChecker.CheckCompatibilityWithAppCodeFolder(dataTypeDescriptor); if (!compatibilityCheckResult.Successful) { errorMessage = _oldDataTypeDescriptor == null ? Texts.CompileErrorWhileAddingType : Texts.CompileErrorWhileChangingType; errorMessage += compatibilityCheckResult.ErrorMessage; + return false; } errorMessage = string.Empty; + return true; } - - /// public bool ValidateNewFieldDescriptors(IEnumerable newDataFieldDescriptors, string keyFieldName, out string message) { @@ -383,45 +348,41 @@ public bool ValidateNewFieldDescriptors(IEnumerable newData message = null; - if (!newDataFieldDescriptors.Any(f => f.Name != keyFieldName)) + if (newDataFieldDescriptors.All(f => f.Name == keyFieldName)) { message = Texts.MissingFields; + return false; } if (keyFieldName != IdFieldName && newDataFieldDescriptors.Any(dfd => dfd.Name == IdFieldName)) { message = Texts.FieldNameCannotBeUsed(IdFieldName); + return false; } return true; } - /// public static string GetCompositionDescriptionPropertyName(Type compositionType) { return CompositionDescriptionFieldName; } - - /// public static PropertyInfo GetCompositionDescriptionPropertyInfo(Type compositionType) { return compositionType.GetPropertiesRecursively().Single(f => f.Name == CompositionDescriptionFieldName); } - - /// public static PropertyInfo GetPageReferencePropertyInfo(Type compositionType) { return compositionType.GetPropertiesRecursively().Single(f => f.Name == PageReferenceFieldName); } - /// public void SetNewTypeFullName(string typeName, string typeNamespace) { @@ -432,8 +393,6 @@ public void SetNewTypeFullName(string typeName, string typeNamespace) _newTypeNamespace = typeNamespace; } - - /// public void SetNewTypeTitle(string typeTitle) { @@ -442,22 +401,24 @@ public void SetNewTypeTitle(string typeTitle) _newTypeTitle = typeTitle; } - - /// public void SetNewInternalUrlPrefix(string internalUrlPrefix) { _newInternalUrlPrefix = internalUrlPrefix; } - - /// - public void SetCachable(bool cachable) + [Obsolete("SetCacheable")] + public void SetCachable(bool cacheable) { - _cachable = cachable; + SetCacheable(cacheable); } + /// + public void SetCacheable(bool cacheable) + { + _cacheable = cacheable; + } /// public void SetSearchable(bool searchable) @@ -465,7 +426,6 @@ public void SetSearchable(bool searchable) _searchable = searchable; } - /// public void SetPublishControlled(bool isPublishControlled) { @@ -474,8 +434,6 @@ public void SetPublishControlled(bool isPublishControlled) _publishControlled = isPublishControlled; } - - /// public void SetLocalizedControlled(bool isLocalizedControlled) { @@ -484,7 +442,6 @@ public void SetLocalizedControlled(bool isLocalizedControlled) _localizedControlled = isLocalizedControlled; } - /// [Obsolete("Left for backward compatibility")] public void SetNewFieldDescriptors(IEnumerable newDataFieldDescriptors, string labelFieldName) @@ -496,8 +453,6 @@ public void SetNewFieldDescriptors(IEnumerable newDataField SetNewFieldDescriptors(fields.Concat(newDataFieldDescriptors), IdFieldName, labelFieldName); } - - /// public void SetNewFieldDescriptors(IEnumerable newDataFieldDescriptors, string keyFieldName, string labelFieldName) { @@ -513,68 +468,62 @@ public void SetNewFieldDescriptors(IEnumerable newDataField } } - - /// public void SetForeignKeyReference(Type targetDataType, DataAssociationType dataAssociationType) { - if (dataAssociationType == DataAssociationType.None) throw new ArgumentException("dataAssociationType"); + if (dataAssociationType == DataAssociationType.None) + { + throw new ArgumentException("dataAssociationType"); + } - DataTypeDescriptor targetDataTypeDescriptor = DynamicTypeManager.GetDataTypeDescriptor(targetDataType); + var targetDataTypeDescriptor = DynamicTypeManager.GetDataTypeDescriptor(targetDataType); SetForeignKeyReference(targetDataTypeDescriptor, dataAssociationType); } - - /// public void SetForeignKeyReference(DataTypeDescriptor targetDataTypeDescriptor, DataAssociationType dataAssociationType) { - if (dataAssociationType == DataAssociationType.None) throw new ArgumentException("dataAssociationType"); + if (dataAssociationType == DataAssociationType.None) + { + throw new ArgumentException("dataAssociationType"); + } - if ((dataAssociationType == DataAssociationType.Aggregation || dataAssociationType == DataAssociationType.Composition) - && _pageMetaDataDescriptionForeignKeyDataFieldDescriptor != null) + if ((dataAssociationType == DataAssociationType.Aggregation || dataAssociationType == DataAssociationType.Composition) && _pageMetaDataDescriptionForeignKeyDataFieldDescriptor != null) { throw new InvalidOperationException("The type already has a foreign key reference"); } - - Type targetType = TypeManager.GetType(targetDataTypeDescriptor.TypeManagerTypeName); string fieldName = null; + + var targetType = TypeManager.GetType(targetDataTypeDescriptor.TypeManagerTypeName); if (targetType == typeof(IPage)) { fieldName = PageReferenceFieldName; + _dataAssociationType = dataAssociationType; } - string foreignKeyFieldName; - _foreignKeyDataFieldDescriptor = CreateReferenceDataFieldDescriptor(targetDataTypeDescriptor, out foreignKeyFieldName, fieldName); + _foreignKeyDataFieldDescriptor = CreateReferenceDataFieldDescriptor(targetDataTypeDescriptor, out var foreignKeyFieldName, fieldName); if (dataAssociationType != DataAssociationType.None) { - _dataTypeAssociationDescriptor = new DataTypeAssociationDescriptor( - targetType, - foreignKeyFieldName, - dataAssociationType - ); + _dataTypeAssociationDescriptor = new DataTypeAssociationDescriptor(targetType, foreignKeyFieldName, dataAssociationType); } if (dataAssociationType == DataAssociationType.Composition) { - DataTypeDescriptor compositionRuleDataTypeDescriptor = DynamicTypeManager.GetDataTypeDescriptor(typeof(IPageMetaDataDefinition)); + var compositionRuleDataTypeDescriptor = DynamicTypeManager.GetDataTypeDescriptor(typeof(IPageMetaDataDefinition)); _pageMetaDataDescriptionForeignKeyDataFieldDescriptor = CreateWeakReferenceDataFieldDescriptor(compositionRuleDataTypeDescriptor, compositionRuleDataTypeDescriptor.Fields["Name"], CompositionDescriptionFieldName); } } - - /// public bool TryValidateUpdate(bool originalTypeDataExists, out string errorMessage) { if (_oldDataTypeDescriptor != null) { - if (_newLabelFieldName == null) { _newLabelFieldName = KeyFieldName; @@ -594,12 +543,13 @@ public bool TryValidateUpdate(bool originalTypeDataExists, out string errorMessa return ValidateByCompile(out errorMessage); } - - /// public void CreateType(bool originalTypeHasData) { - if (_typeCreated) throw new InvalidOperationException("The type can only be created once"); + if (_typeCreated) + { + throw new InvalidOperationException("The type can only be created once"); + } try { @@ -623,10 +573,7 @@ public void CreateType(bool originalTypeHasData) } else { - string errorMessage; - - - UpdateOldType(false, originalTypeHasData, out errorMessage); + UpdateOldType(false, originalTypeHasData, out _); } } finally @@ -635,8 +582,6 @@ public void CreateType(bool originalTypeHasData) } } - - /// public Type InterfaceType { @@ -648,7 +593,6 @@ public Type InterfaceType } } - /// public static void SetNewIdFieldValue(IData data) { @@ -658,8 +602,8 @@ public static void SetNewIdFieldValue(IData data) foreach (var keyProperty in keyProperties) { - bool hasDefaultFieldValueAttribute = keyProperty.GetCustomAttributesRecursively().Any(); - bool hasNewInstanceDefaultFieldValueAttribute = keyProperty.GetCustomAttributesRecursively().Any(); + var hasDefaultFieldValueAttribute = keyProperty.GetCustomAttributesRecursively().Any(); + var hasNewInstanceDefaultFieldValueAttribute = keyProperty.GetCustomAttributesRecursively().Any(); if (!hasDefaultFieldValueAttribute && !hasNewInstanceDefaultFieldValueAttribute) { @@ -677,25 +621,22 @@ public static void SetNewIdFieldValue(IData data) } } - - private void Initialize() { if (_oldDataTypeDescriptor.DataAssociations.Count > 0) { - DataTypeAssociationDescriptor dataTypeAssociationDescriptor = _oldDataTypeDescriptor.DataAssociations.Single(); + var dataTypeAssociationDescriptor = _oldDataTypeDescriptor.DataAssociations.Single(); _associatedType = dataTypeAssociationDescriptor.AssociatedInterfaceType; } - - foreach (DataFieldDescriptor dataFieldDescriptor in _oldDataTypeDescriptor.Fields) + foreach (var dataFieldDescriptor in _oldDataTypeDescriptor.Fields) { if (dataFieldDescriptor.ForeignKeyReferenceTypeName != null) { if (_associatedType != null) { - string associatedTypeTypeName = TypeManager.SerializeType(_associatedType); + var associatedTypeTypeName = TypeManager.SerializeType(_associatedType); if (dataFieldDescriptor.ForeignKeyReferenceTypeName == associatedTypeTypeName) { @@ -711,12 +652,10 @@ private void Initialize() } } - _publishControlled = this.IsPublishControlled; - _localizedControlled = this.IsLocalizedControlled; + _publishControlled = IsPublishControlled; + _localizedControlled = IsLocalizedControlled; } - - private void CreateNewType() { _newDataTypeDescriptor = CreateNewDataTypeDescriptor(); @@ -724,12 +663,11 @@ private void CreateNewType() GeneratedTypesFacade.GenerateNewType(_newDataTypeDescriptor); } - private bool IsCSharpValidIdentifier(string name) + private static bool IsCSharpValidIdentifier(string name) { return CSharpCodeProviderFactory.CreateCompiler().IsValidIdentifier(name); } - private bool UpdateOldType(bool validateOnly, bool originalTypeDataExists, out string errorMessage) { errorMessage = ""; @@ -752,11 +690,9 @@ private bool UpdateOldType(bool validateOnly, bool originalTypeDataExists, out s GeneratedTypesFacade.UpdateType(new UpdateDataTypeDescriptor(_oldDataTypeDescriptor, _newDataTypeDescriptor, originalTypeDataExists)); - return true; + return true; } - - private DataTypeDescriptor CreateNewDataTypeDescriptor() { return CreateNewDataTypeDescriptor( @@ -765,7 +701,7 @@ private DataTypeDescriptor CreateNewDataTypeDescriptor() _newTypeTitle, _newLabelFieldName, _newInternalUrlPrefix, - _cachable, + _cacheable, _searchable, _publishControlled, _localizedControlled, @@ -775,8 +711,6 @@ private DataTypeDescriptor CreateNewDataTypeDescriptor() _pageMetaDataDescriptionForeignKeyDataFieldDescriptor); } - - private DataTypeDescriptor CreateNewDataTypeDescriptor( string typeNamespace, string typeName, @@ -792,7 +726,7 @@ private DataTypeDescriptor CreateNewDataTypeDescriptor( DataTypeAssociationDescriptor dataTypeAssociationDescriptor, DataFieldDescriptor compositionRuleForeignKeyDataFieldDescriptor) { - Guid id = Guid.NewGuid(); + var id = Guid.NewGuid(); var dataTypeDescriptor = new DataTypeDescriptor(id, typeNamespace, typeName, true) { Cachable = cachable, @@ -835,7 +769,7 @@ private DataTypeDescriptor CreateNewDataTypeDescriptor( // dataTypeDescriptor.KeyPropertyNames.Add(IdFieldName); //} - foreach (DataFieldDescriptor dataFieldDescriptor in dataFieldDescriptors) + foreach (var dataFieldDescriptor in dataFieldDescriptors) { dataTypeDescriptor.Fields.Add(dataFieldDescriptor); } @@ -845,7 +779,7 @@ private DataTypeDescriptor CreateNewDataTypeDescriptor( dataTypeDescriptor.KeyPropertyNames.Add(_newKeyFieldName); } - int position = 100; + var position = 100; if (_foreignKeyDataFieldDescriptor != null) { _foreignKeyDataFieldDescriptor.Position = position++; @@ -857,7 +791,6 @@ private DataTypeDescriptor CreateNewDataTypeDescriptor( } } - return dataTypeDescriptor; } @@ -867,16 +800,15 @@ private DataTypeDescriptor CreateNewDataTypeDescriptor( /// public static DataFieldDescriptor BuildIdField() { - var idFieldDescriptor = new DataFieldDescriptor(Guid.NewGuid(), IdFieldName, StoreFieldType.Guid, typeof (Guid)); - idFieldDescriptor.DataUrlProfile = new DataUrlProfile { Order = 0 }; - return idFieldDescriptor; - } + var idFieldDescriptor = new DataFieldDescriptor(Guid.NewGuid(), IdFieldName, StoreFieldType.Guid, typeof(Guid)) + { + DataUrlProfile = new DataUrlProfile { Order = 0 } + }; - private string KeyFieldName - { - get { return _newKeyFieldName ?? IdFieldName; } + return idFieldDescriptor; } + private string KeyFieldName => _newKeyFieldName ?? IdFieldName; //private DataFieldDescriptor BuildKeyFieldDescriptor() //{ @@ -907,23 +839,19 @@ private string KeyFieldName // return result; //} - private DataTypeDescriptor CreateUpdatedDataTypeDescriptor() { - var dataTypeDescriptor = new DataTypeDescriptor( - _oldDataTypeDescriptor.DataTypeId, _newTypeNamespace, _newTypeName, true) + var dataTypeDescriptor = new DataTypeDescriptor(_oldDataTypeDescriptor.DataTypeId, _newTypeNamespace, _newTypeName, true) { - Cachable = _cachable, + Cachable = _cacheable, Searchable = _searchable }; dataTypeDescriptor.DataScopes.Add(DataScopeIdentifier.Public); - - Type[] indirectlyInheritedInterfaces = { - typeof(IPublishControlled), typeof(ILocalizedControlled), + typeof(IPublishControlled), typeof(ILocalizedControlled), typeof(IPageData), typeof(IPageFolderData), typeof(IPageMetaData) }; @@ -936,7 +864,6 @@ private DataTypeDescriptor CreateUpdatedDataTypeDescriptor() } } - if (_publishControlled && _dataAssociationType != DataAssociationType.Composition) { dataTypeDescriptor.AddSuperInterface(typeof(IPublishControlled)); @@ -947,7 +874,6 @@ private DataTypeDescriptor CreateUpdatedDataTypeDescriptor() dataTypeDescriptor.AddSuperInterface(typeof(ILocalizedControlled)); } - if (_oldDataTypeDescriptor.SuperInterfaces.Contains(typeof(IPageFolderData))) { dataTypeDescriptor.AddSuperInterface(typeof(IPageData)); @@ -967,12 +893,12 @@ private DataTypeDescriptor CreateUpdatedDataTypeDescriptor() //dataTypeDescriptor.Fields.Add(idDataFieldDescriptor); //dataTypeDescriptor.KeyPropertyNames.Add(KeyFieldName); - + } dataTypeDescriptor.Title = _newTypeTitle; - foreach (DataFieldDescriptor dataFieldDescriptor in _newDataFieldDescriptors) + foreach (var dataFieldDescriptor in _newDataFieldDescriptors) { dataTypeDescriptor.Fields.Add(dataFieldDescriptor); } @@ -982,13 +908,13 @@ private DataTypeDescriptor CreateUpdatedDataTypeDescriptor() dataTypeDescriptor.KeyPropertyNames.Add(KeyFieldName); } - dataTypeDescriptor.LabelFieldName = _newLabelFieldName; dataTypeDescriptor.InternalUrlPrefix = _newInternalUrlPrefix; dataTypeDescriptor.DataAssociations.AddRange(_oldDataTypeDescriptor.DataAssociations); - int position = 100; + var position = 100; + if (_foreignKeyDataFieldDescriptor != null) { _foreignKeyDataFieldDescriptor.Position = position++; @@ -1002,20 +928,13 @@ private DataTypeDescriptor CreateUpdatedDataTypeDescriptor() return dataTypeDescriptor; } - - - private DataFieldDescriptor CreateWeakReferenceDataFieldDescriptor(DataTypeDescriptor targetDataTypeDescriptor, DataFieldDescriptor targetDataFieldDescriptor, string fieldName) + private static DataFieldDescriptor CreateWeakReferenceDataFieldDescriptor(DataTypeDescriptor targetDataTypeDescriptor, DataFieldDescriptor targetDataFieldDescriptor, string fieldName) { TypeManager.GetType(targetDataTypeDescriptor.TypeManagerTypeName); - WidgetFunctionProvider widgetFunctionProvider = StandardWidgetFunctions.TextBoxWidget; + var widgetFunctionProvider = StandardWidgetFunctions.TextBoxWidget; - return new DataFieldDescriptor( - Guid.NewGuid(), - fieldName, - targetDataFieldDescriptor.StoreType, - targetDataFieldDescriptor.InstanceType - ) + return new DataFieldDescriptor(Guid.NewGuid(), fieldName, targetDataFieldDescriptor.StoreType, targetDataFieldDescriptor.InstanceType) { IsNullable = targetDataFieldDescriptor.IsNullable, DefaultValue = targetDataFieldDescriptor.DefaultValue, @@ -1029,26 +948,18 @@ private DataFieldDescriptor CreateWeakReferenceDataFieldDescriptor(DataTypeDescr }; } - - - private DataFieldDescriptor CreateReferenceDataFieldDescriptor(DataTypeDescriptor targetDataTypeDescriptor, out string foreignKeyFieldName, string fieldName = null) + private static DataFieldDescriptor CreateReferenceDataFieldDescriptor(DataTypeDescriptor targetDataTypeDescriptor, out string foreignKeyFieldName, string fieldName = null) { - Type targetType = TypeManager.GetType(targetDataTypeDescriptor.TypeManagerTypeName); - string targetKeyFieldName = targetDataTypeDescriptor.KeyPropertyNames.First(); + var targetType = TypeManager.GetType(targetDataTypeDescriptor.TypeManagerTypeName); + var targetKeyFieldName = targetDataTypeDescriptor.KeyPropertyNames.First(); - DataFieldDescriptor targetKeyDataFieldDescriptor = targetDataTypeDescriptor.Fields[targetKeyFieldName]; + var targetKeyDataFieldDescriptor = targetDataTypeDescriptor.Fields[targetKeyFieldName]; - foreignKeyFieldName = fieldName ?? - $"{targetDataTypeDescriptor.Name}{targetKeyFieldName}ForeignKey"; + foreignKeyFieldName = fieldName ?? $"{targetDataTypeDescriptor.Name}{targetKeyFieldName}ForeignKey"; var widgetFunctionProvider = StandardWidgetFunctions.GetDataReferenceWidget(targetType); - return new DataFieldDescriptor( - Guid.NewGuid(), - foreignKeyFieldName, - targetKeyDataFieldDescriptor.StoreType, - targetKeyDataFieldDescriptor.InstanceType - ) + return new DataFieldDescriptor(Guid.NewGuid(), foreignKeyFieldName, targetKeyDataFieldDescriptor.StoreType, targetKeyDataFieldDescriptor.InstanceType) { IsNullable = targetKeyDataFieldDescriptor.IsNullable, DefaultValue = targetKeyDataFieldDescriptor.DefaultValue, @@ -1063,27 +974,23 @@ private DataFieldDescriptor CreateReferenceDataFieldDescriptor(DataTypeDescripto }; } - - private bool IsDataFieldBindable(DataTypeDescriptor dataTypeDescriptor, DataFieldDescriptor dataFieldDescriptor) { if (dataFieldDescriptor.Inherited) { - Type superInterface = dataTypeDescriptor.SuperInterfaces.FirstOrDefault(type => type.GetProperty(dataFieldDescriptor.Name) != null); - - if(superInterface != null && superInterface.Assembly == typeof(IData).Assembly) + var superInterface = dataTypeDescriptor.SuperInterfaces.FirstOrDefault(type => type.GetProperty(dataFieldDescriptor.Name) != null); + if (superInterface != null && superInterface.Assembly == typeof(IData).Assembly) { return false; } } - if ((dataFieldDescriptor.Name == IdFieldName || dataFieldDescriptor.Name == CompositionDescriptionFieldName) - && dataTypeDescriptor.IsPageMetaDataType) + if ((dataFieldDescriptor.Name == IdFieldName || dataFieldDescriptor.Name == CompositionDescriptionFieldName) && dataTypeDescriptor.IsPageMetaDataType) { return false; } - if (PageFolderFacade.GetAllFolderTypes().Contains(this._oldType) && dataFieldDescriptor.Name == PageReferenceFieldName) + if (PageFolderFacade.GetAllFolderTypes().Contains(_oldType) && dataFieldDescriptor.Name == PageReferenceFieldName) { return false; } @@ -1091,11 +998,9 @@ private bool IsDataFieldBindable(DataTypeDescriptor dataTypeDescriptor, DataFiel if (dataFieldDescriptor.ForeignKeyReferenceTypeName != null) { var dataTypeAssociationDescriptor = dataTypeDescriptor.DataAssociations.FirstOrDefault(); - if (dataTypeAssociationDescriptor != null) { - if (!this.AllowForeignKeyEditing && - dataFieldDescriptor.Name == dataTypeAssociationDescriptor.ForeignKeyPropertyName) + if (!AllowForeignKeyEditing && dataFieldDescriptor.Name == dataTypeAssociationDescriptor.ForeignKeyPropertyName) { return false; } diff --git a/Composite/Functions/Inline/InlineFunctionHelper.cs b/Composite/Functions/Inline/InlineFunctionHelper.cs index 2e61134ace..dc4bed71fa 100644 --- a/Composite/Functions/Inline/InlineFunctionHelper.cs +++ b/Composite/Functions/Inline/InlineFunctionHelper.cs @@ -1,11 +1,13 @@ using System; using System.CodeDom.Compiler; using System.Collections.Generic; +using System.ComponentModel; using System.IO; using System.Linq; using System.Reflection; using System.Threading; using System.Web.Hosting; +using Composite.Core; using Composite.Core.Configuration; using Composite.Core.Extensions; using Composite.Core.IO; @@ -13,25 +15,20 @@ using Composite.Core.Types; using Composite.Data; using Composite.Data.Types; -using Microsoft.CSharp; -using Composite.Core; using Texts = Composite.Core.ResourceSystem.LocalizationFiles.Composite_Plugins_MethodBasedFunctionProviderElementProvider; namespace Composite.Functions.Inline { - /// - /// + /// /// - [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] + [EditorBrowsable(EditorBrowsableState.Never)] public static class InlineFunctionHelper { - private static readonly string LogTitle = typeof (InlineFunctionHelper).Name; + private static readonly string LogTitle = typeof(InlineFunctionHelper).Name; /// - public static string MethodClassContainerName { get { return "InlineMethodFunction"; } } - - + public static string MethodClassContainerName => "InlineMethodFunction"; /// public static MethodInfo Create(IInlineFunction function, string code = null, InlineFunctionCreateMethodErrorHandler createMethodErrorHandler = null, List selectedAssemblies = null) @@ -59,39 +56,40 @@ public static MethodInfo Create(IInlineFunction function, string code = null, In } } - CompilerParameters compilerParameters = new CompilerParameters(); - compilerParameters.GenerateExecutable = false; - compilerParameters.GenerateInMemory = true; + var compilerParameters = new CompilerParameters + { + GenerateExecutable = false, + GenerateInMemory = true + }; if (selectedAssemblies == null) { - IEnumerable assemblyReferences = - DataFacade.GetData(f => f.Function == function.Id).Evaluate(); + IEnumerable assemblyReferences = DataFacade.GetData(f => f.Function == function.Id).Evaluate(); - foreach (IInlineFunctionAssemblyReference assemblyReference in assemblyReferences) + foreach (var assemblyReference in assemblyReferences) { - string assemblyPath = GetAssemblyFullPath(assemblyReference.Name, assemblyReference.Location); + var assemblyPath = GetAssemblyFullPath(assemblyReference.Name, assemblyReference.Location); + compilerParameters.ReferencedAssemblies.Add(assemblyPath); } } else { - foreach (string reference in selectedAssemblies) + foreach (var reference in selectedAssemblies) { compilerParameters.ReferencedAssemblies.Add(reference); } } - - Assembly appCodeAssembly = AssemblyFacade.GetAppCodeAssembly(); + var appCodeAssembly = AssemblyFacade.GetAppCodeAssembly(); if (appCodeAssembly != null) { compilerParameters.ReferencedAssemblies.Add(appCodeAssembly.Location); } var compiler = CSharpCodeProviderFactory.CreateCompiler(); - CompilerResults results = compiler.CompileAssemblyFromSource(compilerParameters, code); + var results = compiler.CompileAssemblyFromSource(compilerParameters, code); if (results.Errors.HasErrors) { foreach (CompilerError error in results.Errors) @@ -109,11 +107,10 @@ public static MethodInfo Create(IInlineFunction function, string code = null, In return null; } - - Type type = results.CompiledAssembly.GetTypes().SingleOrDefault(f => f.Name == MethodClassContainerName); + var type = results.CompiledAssembly.GetTypes().SingleOrDefault(f => f.Name == MethodClassContainerName); if (type == null) { - string message = Texts.CSharpInlineFunction_OnMissingContainerType(MethodClassContainerName); + var message = Texts.CSharpInlineFunction_OnMissingContainerType(MethodClassContainerName); if (createMethodErrorHandler != null) { @@ -129,7 +126,7 @@ public static MethodInfo Create(IInlineFunction function, string code = null, In if (type.Namespace != function.Namespace) { - string message = Texts.CSharpInlineFunction_OnNamespaceMismatch(type.Namespace, function.Namespace); + var message = Texts.CSharpInlineFunction_OnNamespaceMismatch(type.Namespace, function.Namespace); if (createMethodErrorHandler != null) { @@ -143,10 +140,10 @@ public static MethodInfo Create(IInlineFunction function, string code = null, In return null; } - MethodInfo methodInfo = type.GetMethods(BindingFlags.Public | BindingFlags.Static).SingleOrDefault(f => f.Name == function.Name); + var methodInfo = type.GetMethods(BindingFlags.Public | BindingFlags.Static).SingleOrDefault(f => f.Name == function.Name); if (methodInfo == null) { - string message = Texts.CSharpInlineFunction_OnMissionMethod(function.Name, MethodClassContainerName); + var message = Texts.CSharpInlineFunction_OnMissionMethod(function.Name, MethodClassContainerName); if (createMethodErrorHandler != null) { @@ -163,54 +160,55 @@ public static MethodInfo Create(IInlineFunction function, string code = null, In return methodInfo; } - private static void LogMessageIfNotShuttingDown(IInlineFunction function, string message) { if (!HostingEnvironment.ApplicationHost.ShutdownInitiated()) { - Log.LogWarning(LogTitle, string.Format("{0}.{1} : {2}", function.Namespace, function.Name, message)); + Log.LogWarning(LogTitle, $"{function.Namespace}.{function.Name} : {message}"); } } - /// public static IEnumerable DefaultAssemblies { get { - string systemPath = Path.GetDirectoryName(typeof(String).Assembly.Location); - + var systemPath = Path.GetDirectoryName(typeof(String).Assembly.Location); - yield return Path.Combine(systemPath, "System.dll"); - yield return Path.Combine(systemPath, "System.Core.dll"); - yield return Path.Combine(systemPath, "System.Xml.dll"); - yield return Path.Combine(systemPath, "System.Xml.Linq.dll"); - yield return Path.Combine(systemPath, "System.Web.dll"); + yield return Path.Combine(systemPath, "System.dll"); + yield return Path.Combine(systemPath, "System.Core.dll"); + yield return Path.Combine(systemPath, "System.Xml.dll"); + yield return Path.Combine(systemPath, "System.Xml.Linq.dll"); + yield return Path.Combine(systemPath, "System.Web.dll"); yield return Path.Combine(PathUtil.Resolve(GlobalSettingsFacade.BinDirectory), "Composite.dll"); - string compositeGeneretedPath = Path.Combine(PathUtil.Resolve(GlobalSettingsFacade.BinDirectory), "Composite.Generated.dll"); - if (C1File.Exists(compositeGeneretedPath)) yield return compositeGeneretedPath; + var compositeGeneretedPath = Path.Combine(PathUtil.Resolve(GlobalSettingsFacade.BinDirectory), "Composite.Generated.dll"); + if (C1File.Exists(compositeGeneretedPath)) + { + yield return compositeGeneretedPath; + } - string compositeWorkflowsPath = Path.Combine(PathUtil.Resolve(GlobalSettingsFacade.BinDirectory), "Composite.Workflows.dll"); - if (C1File.Exists(compositeWorkflowsPath)) yield return compositeWorkflowsPath; + var compositeWorkflowsPath = Path.Combine(PathUtil.Resolve(GlobalSettingsFacade.BinDirectory), "Composite.Workflows.dll"); + if (C1File.Exists(compositeWorkflowsPath)) + { + yield return compositeWorkflowsPath; + } } } - internal static string GetSourceFilePath(this IInlineFunction function) { return Path.Combine(PathUtil.Resolve(GlobalSettingsFacade.InlineCSharpFunctionDirectory), function.CodePath); } - /// public static string GetFunctionCode(this IInlineFunction function) { - string filepath = GetSourceFilePath(function); + var filepath = GetSourceFilePath(function); // Making 5 attempts to read the file - for (int i = 5; i > 0; i--) + for (var i = 5; i > 0; i--) { try { @@ -230,49 +228,41 @@ public static string GetFunctionCode(this IInlineFunction function) throw new InvalidOperationException("This line should not be reachable"); } - - /// public static void DeleteFunctionCode(this IInlineFunction function) { - string filepath = GetSourceFilePath(function); + var filepath = GetSourceFilePath(function); FileUtils.Delete(filepath); } - - /// - public static void SetFunctinoCode(this IInlineFunction function, string content) + public static void SetFunctionCode(this IInlineFunction function, string content) { - string directoryPath = PathUtil.Resolve(GlobalSettingsFacade.InlineCSharpFunctionDirectory); + var directoryPath = PathUtil.Resolve(GlobalSettingsFacade.InlineCSharpFunctionDirectory); if (C1Directory.Exists(directoryPath) == false) { C1Directory.CreateDirectory(directoryPath); } - string filepath = Path.Combine(directoryPath, function.CodePath); + var filepath = Path.Combine(directoryPath, function.CodePath); C1File.WriteAllText(filepath, content); } - - /// public static void FunctionRenamed(IInlineFunction newFunction, IInlineFunction oldFunction) { newFunction.UpdateCodePath(); - string directoryPath = PathUtil.Resolve(GlobalSettingsFacade.InlineCSharpFunctionDirectory); + var directoryPath = PathUtil.Resolve(GlobalSettingsFacade.InlineCSharpFunctionDirectory); - string oldFilepath = Path.Combine(directoryPath, oldFunction.CodePath); - string newFilepath = Path.Combine(directoryPath, newFunction.CodePath); + var oldFilepath = Path.Combine(directoryPath, oldFunction.CodePath); + var newFilepath = Path.Combine(directoryPath, newFunction.CodePath); C1File.Move(oldFilepath, newFilepath); } - - /// public static void UpdateCodePath(this IInlineFunction function) { @@ -285,36 +275,32 @@ public static void UpdateCodePath(this IInlineFunction function) function.CodePath += function.Name + ".cs"; } - - /// public static IEnumerable GetReferencableAssemblies() { - string path = Path.GetDirectoryName(typeof(String).Assembly.Location); - foreach (string file in Directory.GetFiles(path, "System*.dll")) + var path = Path.GetDirectoryName(typeof(String).Assembly.Location); + foreach (var file in Directory.GetFiles(path, "System*.dll")) { yield return file; } - foreach (string file in AssemblyFacade.GetAssembliesFromBin(true)) + foreach (var file in AssemblyFacade.GetAssembliesFromBin(true)) { yield return file; } } - - /// public static string GetAssemblyLocation(string fullPath) { - string systemPath = Path.GetDirectoryName(typeof(String).Assembly.Location).ToLowerInvariant(); + var systemPath = Path.GetDirectoryName(typeof(String).Assembly.Location).ToLowerInvariant(); if (fullPath.ToLowerInvariant().StartsWith(systemPath)) { return "System"; } - string binPath = PathUtil.Resolve(GlobalSettingsFacade.BinDirectory).ToLowerInvariant(); + var binPath = PathUtil.Resolve(GlobalSettingsFacade.BinDirectory).ToLowerInvariant(); if (fullPath.ToLowerInvariant().StartsWith(binPath)) { return "Bin"; @@ -323,8 +309,6 @@ public static string GetAssemblyLocation(string fullPath) throw new NotImplementedException(); } - - /// public static string GetAssemblyFullPath(string filename, string location) { From dd56f8eab695ae92cbf5a55c0ee80e08f6dea657 Mon Sep 17 00:00:00 2001 From: vadym-hyryn <57723696+vadym-hyryn@users.noreply.github.com> Date: Wed, 20 May 2020 17:11:52 +0300 Subject: [PATCH 06/20] Function provider for simple methods registration as C1 functions (#737) Integrated CodeBasedFunctionProvider into C1 Extended MethodBasedFunction to be reusable; Rename ServiceFunctionProvider into CodeBasedFunctionProvider (have not found better name, and since we execute functions both from ServiceLocator and Activator, and direct call, somehow should be renamed) Co-authored-by: Taras Nakonechnyi --- Composite/Composite.csproj | 4 ++ .../CodeBasedFunction.cs | 48 +++++++++++++ .../CodeBasedFunctionEntityToken.cs | 31 +++++++++ .../CodeBasedFunctionProvider.cs | 24 +++++++ .../CodeBasedFunctionRegistry.cs | 69 +++++++++++++++++++ .../MethodBasedFunction.cs | 43 ++++++++---- .../Composite/DebugBuild.Composite.config | 1 + .../Composite/ReleaseBuild.Composite.config | 1 + ...seBuild.Composite.config.changeHistory.txt | 13 ++-- 9 files changed, 214 insertions(+), 20 deletions(-) create mode 100644 Composite/Plugins/Functions/FunctionProviders/CodeBasedFunctionProvider/CodeBasedFunction.cs create mode 100644 Composite/Plugins/Functions/FunctionProviders/CodeBasedFunctionProvider/CodeBasedFunctionEntityToken.cs create mode 100644 Composite/Plugins/Functions/FunctionProviders/CodeBasedFunctionProvider/CodeBasedFunctionProvider.cs create mode 100644 Composite/Plugins/Functions/FunctionProviders/CodeBasedFunctionProvider/CodeBasedFunctionRegistry.cs diff --git a/Composite/Composite.csproj b/Composite/Composite.csproj index aac5898d3f..e07b7d5676 100644 --- a/Composite/Composite.csproj +++ b/Composite/Composite.csproj @@ -271,6 +271,10 @@ ASPXCodeBehind + + + + diff --git a/Composite/Plugins/Functions/FunctionProviders/CodeBasedFunctionProvider/CodeBasedFunction.cs b/Composite/Plugins/Functions/FunctionProviders/CodeBasedFunctionProvider/CodeBasedFunction.cs new file mode 100644 index 0000000000..a571085967 --- /dev/null +++ b/Composite/Plugins/Functions/FunctionProviders/CodeBasedFunctionProvider/CodeBasedFunction.cs @@ -0,0 +1,48 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using Composite.C1Console.Security; +using Composite.Core; +using Composite.Functions; +using Composite.Plugins.Functions.FunctionProviders.MethodBasedFunctionProvider; + +namespace Composite.Plugins.Functions.FunctionProviders.CodeBasedFunctionProvider +{ + internal class CodeBasedFunction : MethodBasedFunction, IFunction + { + public static CodeBasedFunction Create(Type type, string methodName, string userNamespace, string userMethodName, string description) + { + var methodInfo = GetMethodInfo(type, methodName, userNamespace, userMethodName, out string _); + return methodInfo == null ? null : new CodeBasedFunction(type, methodInfo, userNamespace, userMethodName, description); + } + + protected CodeBasedFunction(Type type, MethodInfo methodInfo, string userNamespace, string userMethodName, string description) + : base(typeof(CodeBasedFunction).Name) + { + Name = userMethodName; + Namespace = userNamespace; + Type = type; + MethodInfo = methodInfo; + Description = description; + } + public override string Description { get; } + protected override MethodInfo MethodInfo { get; } + public override string Name { get; } + public override string Namespace { get; } + private Type Type { get; } + + public override EntityToken EntityToken => new CodeBasedFunctionEntityToken(this); + + public override object Execute(ParameterList parameters, FunctionContextContainer context) + { + IList arguments = new List(); + foreach (ParameterProfile paramProfile in ParameterProfiles) + { + arguments.Add(parameters.GetParameter(paramProfile.Name, paramProfile.Type)); + } + object instance = MethodInfo.IsStatic ? null : (ServiceLocator.GetService(Type) ?? Activator.CreateInstance(Type)); + return MethodInfo.Invoke(instance, arguments.ToArray()); + } + } +} \ No newline at end of file diff --git a/Composite/Plugins/Functions/FunctionProviders/CodeBasedFunctionProvider/CodeBasedFunctionEntityToken.cs b/Composite/Plugins/Functions/FunctionProviders/CodeBasedFunctionProvider/CodeBasedFunctionEntityToken.cs new file mode 100644 index 0000000000..8f40165e55 --- /dev/null +++ b/Composite/Plugins/Functions/FunctionProviders/CodeBasedFunctionProvider/CodeBasedFunctionEntityToken.cs @@ -0,0 +1,31 @@ +using Composite.C1Console.Security; +using Composite.Functions; + +namespace Composite.Plugins.Functions.FunctionProviders.CodeBasedFunctionProvider +{ + [SecurityAncestorProvider(typeof(StandardFunctionSecurityAncestorProvider))] + internal class CodeBasedFunctionEntityToken : EntityToken + { + internal CodeBasedFunctionEntityToken(CodeBasedFunction function) + { + Id = $"{function.Namespace}.{function.Name}"; + } + + public CodeBasedFunctionEntityToken(string fullName) + { + Id = fullName; + } + + public override string Id { get; } + public static EntityToken Deserialize(string serializedData) + { + DoDeserialize(serializedData, out _, out _, out var id); + return new CodeBasedFunctionEntityToken(id); + } + public override string Serialize() => DoSerialize(); + + public override string Source => string.Empty; + + public override string Type => string.Empty; + } +} \ No newline at end of file diff --git a/Composite/Plugins/Functions/FunctionProviders/CodeBasedFunctionProvider/CodeBasedFunctionProvider.cs b/Composite/Plugins/Functions/FunctionProviders/CodeBasedFunctionProvider/CodeBasedFunctionProvider.cs new file mode 100644 index 0000000000..5405733d67 --- /dev/null +++ b/Composite/Plugins/Functions/FunctionProviders/CodeBasedFunctionProvider/CodeBasedFunctionProvider.cs @@ -0,0 +1,24 @@ +using System.Collections.Generic; +using Composite.Functions; +using Composite.Functions.Plugins.FunctionProvider; + +namespace Composite.Plugins.Functions.FunctionProviders.CodeBasedFunctionProvider +{ + internal class CodeBasedFunctionProvider : IFunctionProvider + { + private static FunctionNotifier _functionNotifier; + + public FunctionNotifier FunctionNotifier + { + set => _functionNotifier = value; + } + + public IEnumerable Functions => CodeBasedFunctionRegistry.Functions; + + internal static void Reload() + { + // Can be called before function provider initialization + _functionNotifier?.FunctionsUpdated(); + } + } +} \ No newline at end of file diff --git a/Composite/Plugins/Functions/FunctionProviders/CodeBasedFunctionProvider/CodeBasedFunctionRegistry.cs b/Composite/Plugins/Functions/FunctionProviders/CodeBasedFunctionProvider/CodeBasedFunctionRegistry.cs new file mode 100644 index 0000000000..8c806e3276 --- /dev/null +++ b/Composite/Plugins/Functions/FunctionProviders/CodeBasedFunctionProvider/CodeBasedFunctionRegistry.cs @@ -0,0 +1,69 @@ +using System.Collections.Concurrent; +using System.Linq; +using Composite.Functions; +using Microsoft.Extensions.DependencyInjection; + +namespace Composite.Plugins.Functions.FunctionProviders.CodeBasedFunctionProvider +{ + /// + /// This class allows to register methods from external assemblies as C1 functions. + /// To register own method for C1 usage: + /// 1. Register a suitable dependency in an appropriate way in case it is needed + /// an object for a method call (recommended) + /// For example: + /// + /// public static void ConfigureServices(IServiceCollection collection) + /// { + /// collection.AddSingleTon(typeof(YourServiceType)); + /// } + /// + /// 2. Register needed method as C1 function with RegisterMethod call + /// + public static class CodeBasedFunctionRegistry + { + internal static readonly ConcurrentBag Functions = new ConcurrentBag(); + + /// + /// Provided to register methods from external assemblies as C1 functions + /// + /// Type of a class, which contains the needed method + /// Name of the method to be registered + /// + /// Set up custom method full name for displaying in C1. + /// For example: TestNamespace.TestClassName.TestMethodName + /// + /// Can be provided a custom description for the method + public static void RegisterMethod(string methodName, string userMethodFullName = null, string description = null) where T : class + { + var type = typeof(T); + + string userNamespace, userMethodName; + + if (string.IsNullOrWhiteSpace(userMethodFullName)) + { + userMethodName = methodName; + userNamespace = type.Namespace; + } + else + { + ParseFunctionName(userMethodFullName, out userNamespace, out userMethodName); + } + + var function = CodeBasedFunction.Create(type, methodName, userNamespace, userMethodName, description); + Functions.Add(function); + CodeBasedFunctionProvider.Reload(); + } + + private static void ParseFunctionName(string userMethodFullName, out string userNamespace, out string userMethodName) + { + string[] parts = userMethodFullName.Split(new[] { '.' }); + + Verify.That(parts.Length > 1, "Missing a function namespace in full function name '{0}'", userMethodFullName); + + Verify.IsFalse(parts.Any(string.IsNullOrWhiteSpace), "Empty full name parts are not allowed"); + + userNamespace = string.Join(".", parts.Take(parts.Length - 1)); + userMethodName = parts.Last(); + } + } +} \ No newline at end of file diff --git a/Composite/Plugins/Functions/FunctionProviders/MethodBasedFunctionProvider/MethodBasedFunction.cs b/Composite/Plugins/Functions/FunctionProviders/MethodBasedFunctionProvider/MethodBasedFunction.cs index bc870e804c..3f8ace6d61 100644 --- a/Composite/Plugins/Functions/FunctionProviders/MethodBasedFunctionProvider/MethodBasedFunction.cs +++ b/Composite/Plugins/Functions/FunctionProviders/MethodBasedFunctionProvider/MethodBasedFunction.cs @@ -16,7 +16,7 @@ namespace Composite.Plugins.Functions.FunctionProviders.MethodBasedFunctionProvi { internal class MethodBasedFunction : IFunction { - private static readonly string LogTitle = typeof(MethodBasedFunction).Name; + private static string LogTitle; private readonly IMethodBasedFunctionInfo _methodBasedFunctionInfo; private readonly Type _type; @@ -25,8 +25,12 @@ internal class MethodBasedFunction : IFunction private string _functionDescription; private object _object; + protected MethodBasedFunction(string logTitle) + { + LogTitle = logTitle; + } - protected MethodBasedFunction(IMethodBasedFunctionInfo info, Type type, MethodInfo methodInfo) + protected MethodBasedFunction(IMethodBasedFunctionInfo info, Type type, MethodInfo methodInfo) : this(typeof(MethodBasedFunction).Name) { _methodBasedFunctionInfo = info; _type = type; @@ -38,10 +42,11 @@ protected MethodBasedFunction(IMethodBasedFunctionInfo info, Type type, MethodIn public static MethodBasedFunction Create(IMethodBasedFunctionInfo info) { Type type = TypeManager.TryGetType(info.Type); + string errorMessage; if (type == null) { - string errorMessage = "Could not find the type '{0}'".FormatWith(info.Type); + errorMessage = "Could not find the type '{0}'".FormatWith(info.Type); // Skipping error log while package installation, the type/method may be available after restart if (!HostingEnvironment.ApplicationHost.ShutdownInitiated()) @@ -52,31 +57,39 @@ public static MethodBasedFunction Create(IMethodBasedFunctionInfo info) return new NotLoadedMethodBasedFunction(info, errorMessage); } - MethodInfo methodInfo = type.GetMethods().FirstOrDefault(mi => mi.Name == info.MethodName); + MethodInfo methodInfo = GetMethodInfo(type, info.MethodName, info.Namespace, info.UserMethodName, out errorMessage); + return methodInfo == null ? new NotLoadedMethodBasedFunction(info, errorMessage) : new MethodBasedFunction(info, type, methodInfo); + } + protected static MethodInfo GetMethodInfo(Type type, string methodName, string @namespace, string userMethodName, out string errorMessage) + { + MethodInfo methodInfo = type.GetMethods().FirstOrDefault(mi => mi.Name == methodName); + + errorMessage = null; if (methodInfo == null) { - string errorMessage = "Could not find the method '{0}' on the the type '{1}'".FormatWith(info.MethodName, info.Type); + errorMessage = "Could not find the method '{0}' on the the type '{1}'".FormatWith(methodName, type.Name); // Skipping error log while package installation, the type/method may be available after restart if (!HostingEnvironment.ApplicationHost.ShutdownInitiated()) { - Log.LogError(LogTitle, GetErrorMessage(info) + errorMessage); + Log.LogError(LogTitle, GetErrorMessage(@namespace, userMethodName) + errorMessage); } - - return new NotLoadedMethodBasedFunction(info, errorMessage); } - - return new MethodBasedFunction(info, type, methodInfo); + return methodInfo; } + private static string GetErrorMessage(string @namespace, string userMethodName) + { + return "Failed to initialize function '{0}.{1}'. ".FormatWith(@namespace, userMethodName); + } private static string GetErrorMessage(IMethodBasedFunctionInfo info) { return "Failed to initialize function '{0}.{1}'. ".FormatWith(info.Namespace, info.UserMethodName); } - public object Execute(ParameterList parameters, FunctionContextContainer context) + public virtual object Execute(ParameterList parameters, FunctionContextContainer context) { IList arguments = new List(); foreach (ParameterProfile paramProfile in ParameterProfiles) @@ -90,14 +103,14 @@ public object Execute(ParameterList parameters, FunctionContextContainer context - public string Name + public virtual string Name { get { return _methodBasedFunctionInfo.UserMethodName; } } - public string Namespace + public virtual string Namespace { get { return _methodBasedFunctionInfo.Namespace; } } @@ -260,7 +273,7 @@ object obj in this.MethodInfo.GetCustomAttributes(typeof (FunctionParameterDescr - private MethodInfo MethodInfo + protected virtual MethodInfo MethodInfo { get { @@ -285,7 +298,7 @@ private object Object - public EntityToken EntityToken + public virtual EntityToken EntityToken { get { diff --git a/Website/App_Data/Composite/DebugBuild.Composite.config b/Website/App_Data/Composite/DebugBuild.Composite.config index 58dd780f4a..95748ac093 100644 --- a/Website/App_Data/Composite/DebugBuild.Composite.config +++ b/Website/App_Data/Composite/DebugBuild.Composite.config @@ -389,6 +389,7 @@ + diff --git a/Website/App_Data/Composite/ReleaseBuild.Composite.config b/Website/App_Data/Composite/ReleaseBuild.Composite.config index 7b8ff8b4b9..5889fa311b 100644 --- a/Website/App_Data/Composite/ReleaseBuild.Composite.config +++ b/Website/App_Data/Composite/ReleaseBuild.Composite.config @@ -383,6 +383,7 @@ + diff --git a/Website/App_Data/Composite/ReleaseBuild.Composite.config.changeHistory.txt b/Website/App_Data/Composite/ReleaseBuild.Composite.config.changeHistory.txt index 6e456a7377..c248ae26f3 100644 --- a/Website/App_Data/Composite/ReleaseBuild.Composite.config.changeHistory.txt +++ b/Website/App_Data/Composite/ReleaseBuild.Composite.config.changeHistory.txt @@ -168,9 +168,12 @@ Changes in 6.0 or later - Changes in 6.6 or later: - -Added to loggingConfiguration/listeners - -Added to loggingConfiguration/specialSources/allEvents/listeners +Changes in 6.6 or later: +-Added to loggingConfiguration/listeners +-Added to loggingConfiguration/specialSources/allEvents/listeners - Changes in 6.7 or later: - -Added to configuration/Composite.C1Console.Forms.Plugins.UiControlFactoryConfiguration/Channels/Channel/Namespaces/Namespace/Factories \ No newline at end of file +Changes in 6.7 or later: +-Added to configuration/Composite.C1Console.Forms.Plugins.UiControlFactoryConfiguration/Channels/Channel/Namespaces/Namespace/Factories + +Changes in 6.9 or later: +-Added to configuration/Composite.Functions.Plugins.FunctionProviderConfiguration/FunctionProviderPlugins \ No newline at end of file From 2d341e52d965b27e9c189ae53f6483ee1d274f5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pauli=20=C3=98ster=C3=B8?= Date: Fri, 29 May 2020 15:49:05 +0200 Subject: [PATCH 07/20] CodeBasedFunctionProvider object creation enhancement MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use ActivatorUtilities.GetServiceOrCreateInstance instead of ServiceLocator.GetService + Activator.CreateInstance since the type in question might not be registered in the ServiceContainer but still require services to be injected into its ctor (by Pauli Østerø ) --- .../CodeBasedFunctionProvider/CodeBasedFunction.cs | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/Composite/Plugins/Functions/FunctionProviders/CodeBasedFunctionProvider/CodeBasedFunction.cs b/Composite/Plugins/Functions/FunctionProviders/CodeBasedFunctionProvider/CodeBasedFunction.cs index a571085967..69f6808d96 100644 --- a/Composite/Plugins/Functions/FunctionProviders/CodeBasedFunctionProvider/CodeBasedFunction.cs +++ b/Composite/Plugins/Functions/FunctionProviders/CodeBasedFunctionProvider/CodeBasedFunction.cs @@ -1,19 +1,20 @@ using System; using System.Collections.Generic; -using System.Linq; using System.Reflection; using Composite.C1Console.Security; using Composite.Core; using Composite.Functions; using Composite.Plugins.Functions.FunctionProviders.MethodBasedFunctionProvider; +using Microsoft.Extensions.DependencyInjection; namespace Composite.Plugins.Functions.FunctionProviders.CodeBasedFunctionProvider { - internal class CodeBasedFunction : MethodBasedFunction, IFunction + internal class CodeBasedFunction : MethodBasedFunction { public static CodeBasedFunction Create(Type type, string methodName, string userNamespace, string userMethodName, string description) { - var methodInfo = GetMethodInfo(type, methodName, userNamespace, userMethodName, out string _); + var methodInfo = GetMethodInfo(type, methodName, userNamespace, userMethodName, out _); + return methodInfo == null ? null : new CodeBasedFunction(type, methodInfo, userNamespace, userMethodName, description); } @@ -26,6 +27,7 @@ protected CodeBasedFunction(Type type, MethodInfo methodInfo, string userNamespa MethodInfo = methodInfo; Description = description; } + public override string Description { get; } protected override MethodInfo MethodInfo { get; } public override string Name { get; } @@ -36,12 +38,14 @@ protected CodeBasedFunction(Type type, MethodInfo methodInfo, string userNamespa public override object Execute(ParameterList parameters, FunctionContextContainer context) { - IList arguments = new List(); + var arguments = new List(); foreach (ParameterProfile paramProfile in ParameterProfiles) { arguments.Add(parameters.GetParameter(paramProfile.Name, paramProfile.Type)); } - object instance = MethodInfo.IsStatic ? null : (ServiceLocator.GetService(Type) ?? Activator.CreateInstance(Type)); + + var instance = MethodInfo.IsStatic ? null : ActivatorUtilities.GetServiceOrCreateInstance(ServiceLocator.ServiceProvider, Type); + return MethodInfo.Invoke(instance, arguments.ToArray()); } } From ea73776f683c067825350b7aa7e657b78660b9cc Mon Sep 17 00:00:00 2001 From: Dmitry Dzygin Date: Tue, 16 Jun 2020 13:30:37 +0200 Subject: [PATCH 08/20] CmsPageHttpHandler fixing public caching not being disabled when a function should prevent it --- Composite/AspNet/CmsPageHttpHandler.cs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/Composite/AspNet/CmsPageHttpHandler.cs b/Composite/AspNet/CmsPageHttpHandler.cs index 56a57043bf..b920322954 100644 --- a/Composite/AspNet/CmsPageHttpHandler.cs +++ b/Composite/AspNet/CmsPageHttpHandler.cs @@ -133,13 +133,8 @@ public void ProcessRequest(HttpContext context) var response = context.Response; - if (preventResponseCaching) - { - context.Response.Cache.SetNoServerCaching(); - } - // Disabling ASP.NET cache if there's a logged-in user - if (consoleUserLoggedIn) + if (consoleUserLoggedIn || preventResponseCaching) { context.Response.Cache.SetCacheability(HttpCacheability.NoCache); } From 5a08a19ba24d1b59e1593278953f49d9cda1801e Mon Sep 17 00:00:00 2001 From: Dmitry Dzygin Date: Tue, 16 Jun 2020 17:38:27 +0200 Subject: [PATCH 09/20] Customizable page route (#748) Making some of the page route related classes reusable Added possibility to extend SitemapNavigator Co-authored-by: Taras Nakonechnyi --- Composite/AspNet/CmsPageSiteMapNode.cs | 2 +- .../Core/Routing/Pages/C1PageRouteHander.cs | 10 ++- .../Pages/SeoFriendlyRedirectRouteHandler.cs | 4 +- Composite/Core/WebClient/UrlUtils.cs | 2 +- Composite/Data/PageNode.cs | 80 +++++++------------ .../PageInternalUrlConverter.cs | 11 ++- .../Routing/Pages/DefaultPageUrlProvider.cs | 16 ++-- 7 files changed, 60 insertions(+), 65 deletions(-) diff --git a/Composite/AspNet/CmsPageSiteMapNode.cs b/Composite/AspNet/CmsPageSiteMapNode.cs index ed7f1716f5..f78cb30f65 100644 --- a/Composite/AspNet/CmsPageSiteMapNode.cs +++ b/Composite/AspNet/CmsPageSiteMapNode.cs @@ -15,7 +15,7 @@ public class CmsPageSiteMapNode : SiteMapNode, ICmsSiteMapNode, ISchemaOrgSiteMa private int? _depth; /// - public CultureInfo Culture { get; } + public CultureInfo Culture { get; protected set; } /// public int? Priority { get; protected set; } diff --git a/Composite/Core/Routing/Pages/C1PageRouteHander.cs b/Composite/Core/Routing/Pages/C1PageRouteHander.cs index 014fb10211..07356b40f7 100644 --- a/Composite/Core/Routing/Pages/C1PageRouteHander.cs +++ b/Composite/Core/Routing/Pages/C1PageRouteHander.cs @@ -18,7 +18,10 @@ namespace Composite.Core.Routing.Pages { - internal class C1PageRouteHandler : IRouteHandler + /// + /// A route handler for the "C1 page" route. + /// + public class C1PageRouteHandler : IRouteHandler { private const string PageHandlerPath = "Renderers/Page.aspx"; private const string PageHandlerVirtualPath = "~/" + PageHandlerPath; @@ -65,12 +68,17 @@ static C1PageRouteHandler() } + /// + /// Creates a new instance of . + /// + /// public C1PageRouteHandler(PageUrlData pageUrlData) { _pageUrlData = pageUrlData; } + /// public IHttpHandler GetHttpHandler(RequestContext requestContext) { var context = requestContext.HttpContext; diff --git a/Composite/Core/Routing/Pages/SeoFriendlyRedirectRouteHandler.cs b/Composite/Core/Routing/Pages/SeoFriendlyRedirectRouteHandler.cs index 63ce5ee806..6abaefd5b3 100644 --- a/Composite/Core/Routing/Pages/SeoFriendlyRedirectRouteHandler.cs +++ b/Composite/Core/Routing/Pages/SeoFriendlyRedirectRouteHandler.cs @@ -1,9 +1,9 @@ -using System.Web; +using System.Web; using System.Web.Routing; namespace Composite.Core.Routing.Pages { - internal class SeoFriendlyRedirectRouteHandler: IRouteHandler + public class SeoFriendlyRedirectRouteHandler: IRouteHandler { private readonly string _redirectUrl; diff --git a/Composite/Core/WebClient/UrlUtils.cs b/Composite/Core/WebClient/UrlUtils.cs index a480854840..d141b4f091 100644 --- a/Composite/Core/WebClient/UrlUtils.cs +++ b/Composite/Core/WebClient/UrlUtils.cs @@ -126,7 +126,7 @@ internal static bool IsRendererRequest(HttpContext httpContext) /// Determines whether the current request is administration console request. /// (Requests to [/virtual path]/Composite/*) /// - internal static bool IsAdminConsoleRequest(string requestPath) + public static bool IsAdminConsoleRequest(string requestPath) { return string.Compare(requestPath, UrlUtils.AdminRootPath, StringComparison.OrdinalIgnoreCase) == 0 || requestPath.StartsWith(UrlUtils.AdminRootPath + "/", StringComparison.OrdinalIgnoreCase); diff --git a/Composite/Data/PageNode.cs b/Composite/Data/PageNode.cs index edc07292e9..9e59d5fc7e 100644 --- a/Composite/Data/PageNode.cs +++ b/Composite/Data/PageNode.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Globalization; using System.Xml.Linq; @@ -21,7 +21,12 @@ public class PageNode private XElement _pageElement; private int? _level; - internal PageNode(IPage page, SitemapNavigatorImplementation sitemapNavigator) + /// + /// Initializes a new instance of . + /// + /// The page. + /// The site map navigator. + public PageNode(IPage page, SitemapNavigatorImplementation sitemapNavigator) { Verify.ArgumentNotNull(page, "page"); @@ -33,80 +38,44 @@ internal PageNode(IPage page, SitemapNavigatorImplementation sitemapNavigator) /// /// The Id of the page /// - public Guid Id - { - get - { - return _page.Id; - } - } + public virtual Guid Id => _page.Id; /// /// The Title of the page /// - public string Title - { - get - { - return _page.Title; - } - } + public virtual string Title => _page.Title; /// /// The Menu Title of the page /// - public string MenuTitle - { - get - { - return string.IsNullOrEmpty(_page.MenuTitle) ? null : _page.MenuTitle; - } - } + public virtual string MenuTitle => string.IsNullOrEmpty(_page.MenuTitle) ? null : _page.MenuTitle; /// /// The time the page was changed last /// - public DateTime ChangedDate - { - get - { - return _page.ChangeDate; - } - } + public virtual DateTime ChangedDate => _page.ChangeDate; /// /// The Description of the page /// - public string Description - { - get - { - return _page.Description; - } - } + public virtual string Description => _page.Description; /// /// Url to this page. /// [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1056:UriPropertiesShouldNotBeStrings")] - public string Url - { - get - { - return PageUrls.BuildUrl(_page); - } - } + public virtual string Url => PageUrls.BuildUrl(_page); /// /// The level this page is placed at in the sitemap. Level 1 is a homepage, level 2 are children of the homepage and so on. /// - public int Level + public virtual int Level { get { @@ -114,7 +83,7 @@ public int Level { PageNode parent = ParentPage; - _level = parent == null ? 1 : parent.Level + 1; + _level = parent?.Level + 1 ?? 1; } return _level.Value; @@ -125,7 +94,7 @@ public int Level /// /// Returns the parent . /// - public PageNode ParentPage + public virtual PageNode ParentPage { get { @@ -143,7 +112,7 @@ public PageNode ParentPage /// /// Returns elements that represent the immediate children of this page. /// - public IEnumerable ChildPages + public virtual IEnumerable ChildPages { get { @@ -165,7 +134,7 @@ public IEnumerable ChildPages /// /// The scope. /// - public IEnumerable GetPageNodes(SitemapScope scope) + public virtual IEnumerable GetPageNodes(SitemapScope scope) { if (scope < SitemapScope.Current || scope > SitemapScope.SiblingsAndSelf) throw new ArgumentOutOfRangeException("scope"); @@ -186,7 +155,7 @@ public IEnumerable GetPageNodes(SitemapScope scope) /// /// The scope. /// - public IEnumerable GetPageIds(SitemapScope scope) + public virtual IEnumerable GetPageIds(SitemapScope scope) { if (scope < SitemapScope.Current || scope > SitemapScope.SiblingsAndSelf) throw new ArgumentOutOfRangeException("scope"); @@ -198,7 +167,7 @@ public IEnumerable GetPageIds(SitemapScope scope) /// XML representing the page and it's decendants. Do NOT modify this structure. To do modifications, clone this first. /// [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", MessageId = "Sitemap")] - public XElement SitemapXml + public virtual XElement SitemapXml { get { @@ -212,6 +181,13 @@ public XElement SitemapXml } + /// + /// Gets the page. + /// + /// The page + public virtual IPage Page => _page; + + /// /// Serialize the page specific state to a string for reading. /// @@ -229,4 +205,4 @@ public override string ToString() this.Level); } } -} +} \ No newline at end of file diff --git a/Composite/Plugins/Routing/InternalUrlConverters/PageInternalUrlConverter.cs b/Composite/Plugins/Routing/InternalUrlConverters/PageInternalUrlConverter.cs index 74800b23ed..6cffbb0338 100644 --- a/Composite/Plugins/Routing/InternalUrlConverters/PageInternalUrlConverter.cs +++ b/Composite/Plugins/Routing/InternalUrlConverters/PageInternalUrlConverter.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; using Composite.Core; using Composite.Core.Extensions; using Composite.Core.Routing; @@ -6,14 +6,18 @@ namespace Composite.Plugins.Routing.InternalUrlConverters { - internal class PageInternalUrlConverter: IInternalUrlConverter + /// + /// Url provider for "internal" page links, f.e. "~/page({Guid})" + /// + public class PageInternalUrlConverter: IInternalUrlConverter { - private static readonly string LogTitle = typeof(MediaInternalUrlConverter).Name; + private static readonly string LogTitle = nameof(PageInternalUrlConverter); private readonly string[] _acceptedUrlPrefixes = { "page(", "Renderers/Page.aspx" }; public IEnumerable AcceptedUrlPrefixes { get { return _acceptedUrlPrefixes; } } + /// public string ToPublicUrl(string internalPageUrl, UrlSpace urlSpace) { PageUrlData pageUrlData; @@ -56,6 +60,7 @@ public string ToPublicUrl(string internalPageUrl, UrlSpace urlSpace) return publicPageUrl; } + /// public IDataReference ToDataReference(string internalUrl) { return null; diff --git a/Composite/Plugins/Routing/Pages/DefaultPageUrlProvider.cs b/Composite/Plugins/Routing/Pages/DefaultPageUrlProvider.cs index 915284973b..9b062ef360 100644 --- a/Composite/Plugins/Routing/Pages/DefaultPageUrlProvider.cs +++ b/Composite/Plugins/Routing/Pages/DefaultPageUrlProvider.cs @@ -21,8 +21,11 @@ namespace Composite.Plugins.Routing.Pages { + /// + /// Default implementation of . + /// [ConfigurationElementType(typeof(NonConfigurablePageUrlProvider))] - internal sealed class DefaultPageUrlProvider: IPageUrlProvider + public class DefaultPageUrlProvider: IPageUrlProvider { public static readonly string UrlMarker_RelativeUrl = "/c1mode(relative)"; public static readonly string UrlMarker_Unpublished = "/c1mode(unpublished)"; @@ -33,6 +36,9 @@ internal sealed class DefaultPageUrlProvider: IPageUrlProvider private static readonly Hashtable, Hashtable> _friendlyUrls = new Hashtable, Hashtable>(); + /// + /// A URL suffix, to be inserted in every page URL, f.e. ".aspx" + /// public static string UrlSuffix { get; private set;} static DefaultPageUrlProvider() @@ -54,6 +60,7 @@ static DefaultPageUrlProvider() DataEvents.OnStoreChanged += (a, b) => LoadUrlSuffix(); } + public DefaultPageUrlProvider() { LoadUrlSuffix(); @@ -469,7 +476,7 @@ private PageUrlData ParsePagePath(string pagePath, PublicationScope publicationS if (page != null) { - return new PageUrlData(page.Id, publicationScope, locale) + return new PageUrlData(page.Id, publicationScope, page.DataSourceId.LocaleScope) { VersionId = page.VersionId, PathInfo = pathInfo @@ -480,7 +487,7 @@ private PageUrlData ParsePagePath(string pagePath, PublicationScope publicationS return null; } - private static IPage TryGetPageByUrlTitlePath(string pagePath, bool pathInfoExtracted, IHostnameBinding hostnameBinding, ref string pathInfo) + private IPage TryGetPageByUrlTitlePath(string pagePath, bool pathInfoExtracted, IHostnameBinding hostnameBinding, ref string pathInfo) { string[] pageUrlTitles = pagePath.Split(new[] {'/'}, StringSplitOptions.RemoveEmptyEntries); @@ -558,7 +565,7 @@ private static IPage TryGetPageByUrlTitlePath(string pagePath, bool pathInfoExtr return currentPage; } - private static IPage FindMatchingPage(Guid parentId, string urlTitle) + protected virtual IPage FindMatchingPage(Guid parentId, string urlTitle) { foreach (var page in GetChildPages(parentId)) { @@ -939,7 +946,6 @@ private static string BuildInternalUrl(PageUrlData pageUrlData) } result.PathInfo = pathInfo; - result["cultureInfo"] = cultureInfo.ToString(); if (pageUrlData.QueryParameters != null) From a9d48225505d591dd7f563dc4acba23184ff1e9e Mon Sep 17 00:00:00 2001 From: Taras Nakonechnyi Date: Thu, 23 Jul 2020 10:39:47 +0300 Subject: [PATCH 10/20] Get actions from IElementActionProvider loaded via DependencyInjection (#750) * Get actions from IElementActionProvider loaded via DependencyInjection --- .../Foundation/ElementActionProviderFacade.cs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/Composite/C1Console/Elements/Foundation/ElementActionProviderFacade.cs b/Composite/C1Console/Elements/Foundation/ElementActionProviderFacade.cs index ede4d420e2..f0751a856a 100644 --- a/Composite/C1Console/Elements/Foundation/ElementActionProviderFacade.cs +++ b/Composite/C1Console/Elements/Foundation/ElementActionProviderFacade.cs @@ -6,6 +6,7 @@ using System.Web; using Composite.C1Console.Actions; using Composite.C1Console.Elements.Foundation.PluginFacades; +using Composite.C1Console.Elements.Plugins.ElementActionProvider; using Composite.C1Console.Events; using Composite.C1Console.Security; using Composite.Core; @@ -326,6 +327,22 @@ public static void AddActions(IEnumerable elements, string providerName Log.LogCritical("ElementActionProviderFacade", ex); } } + + foreach (var elementActionProvider in ServiceLocator.GetServices()) + { + try + { + var actions = elementActionProvider.GetActions(element.ElementHandle.EntityToken); + + element.AddAction(actions); + } + catch (Exception ex) + { + Log.LogCritical(nameof(ElementActionProviderFacade), + $"Failed to add actions from the element action provider '{elementActionProvider.GetType()}'"); + Log.LogCritical(nameof(ElementActionProviderFacade), ex); + } + } } } } From 0f62fc5d1989470ac1e1277529d649038de91f40 Mon Sep 17 00:00:00 2001 From: Taras Nakonechnyi Date: Wed, 5 Aug 2020 08:41:54 +0300 Subject: [PATCH 11/20] =?UTF-8?q?DataReferenceTreeSelector:Take=20data=20f?= =?UTF-8?q?or=20PageReference=20from=20SitemapNav=E2=80=A6=20(#752)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * DataReferenceTreeSelector:Take data for PageReference from SitemapNavigator instead DataFacade.GetDataByUniqueKey --- .../Selectors/DataReferenceTreeSelector.ascx | 93 +++++++++++++++---- 1 file changed, 77 insertions(+), 16 deletions(-) diff --git a/Website/Composite/controls/FormsControls/FormUiControlTemplates/Selectors/DataReferenceTreeSelector.ascx b/Website/Composite/controls/FormsControls/FormUiControlTemplates/Selectors/DataReferenceTreeSelector.ascx index 8a0c71f11b..77d535fdc0 100644 --- a/Website/Composite/controls/FormsControls/FormUiControlTemplates/Selectors/DataReferenceTreeSelector.ascx +++ b/Website/Composite/controls/FormsControls/FormUiControlTemplates/Selectors/DataReferenceTreeSelector.ascx @@ -1,4 +1,4 @@ -<%@ Control Language="C#" Inherits="Composite.Plugins.Forms.WebChannel.UiControlFactories.DataReferenceTreeSelectorTemplateUserControlBase" %> +<%@ Control Language="C#" Inherits="Composite.Plugins.Forms.WebChannel.UiControlFactories.DataReferenceTreeSelectorTemplateUserControlBase" %> <%@ Import Namespace="Composite.Core.Routing" %> <%@ Import Namespace="Composite.Data" %> <%@ Import Namespace="Composite.Data.Types" %> @@ -8,7 +8,7 @@ Date: Wed, 5 Aug 2020 13:52:52 +0300 Subject: [PATCH 12/20] ResxEditor: (#753) -fix language issue -fix not synced resx -fix html encode -add user-select for name and value --- .../content/misc/editors/resxeditor/resxeditor.aspx | 4 ++-- .../content/misc/editors/resxeditor/resxeditor.aspx.cs | 6 +++--- .../content/misc/editors/resxeditor/resxeditor.css | 3 ++- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/Website/Composite/content/misc/editors/resxeditor/resxeditor.aspx b/Website/Composite/content/misc/editors/resxeditor/resxeditor.aspx index 5d459e0b0a..3a2415d910 100644 --- a/Website/Composite/content/misc/editors/resxeditor/resxeditor.aspx +++ b/Website/Composite/content/misc/editors/resxeditor/resxeditor.aspx @@ -1,4 +1,4 @@ -<%@ Page Async="true" Language="C#" Debug="true" AutoEventWireup="true" CodeFile="resxeditor.aspx.cs" Inherits="ResxEditor" +<%@ Page Async="true" Language="C#" Debug="true" AutoEventWireup="true" CodeFile="resxeditor.aspx.cs" Inherits="ResxEditor" ValidateRequest="false" %> <%@ Import Namespace="Composite.Core.ResourceSystem" %> @@ -73,7 +73,7 @@ <% if (OtherCultureExist) { %> - + diff --git a/Website/Composite/content/misc/editors/resxeditor/resxeditor.aspx.cs b/Website/Composite/content/misc/editors/resxeditor/resxeditor.aspx.cs index 0c2fe53ba0..4417e69b9c 100644 --- a/Website/Composite/content/misc/editors/resxeditor/resxeditor.aspx.cs +++ b/Website/Composite/content/misc/editors/resxeditor/resxeditor.aspx.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Globalization; using System.IO; @@ -65,7 +65,7 @@ protected void Page_Load(object sender, EventArgs e) } var loc = CultureInfo.GetCultures(CultureTypes.AllCultures) - .LastOrDefault(f => f.Name != "" && FileName.EndsWith(f.Name + ".Resx", StringComparison.OrdinalIgnoreCase)); + .LastOrDefault(f => f.Name != "" && FileName.EndsWith("." + f.Name + ".Resx", StringComparison.OrdinalIgnoreCase)); if (loc != null) { @@ -226,7 +226,7 @@ private void BindGridView() { Label = n.Key, Original = n.Value, - Translated = (otherculturedic != null) ? otherculturedic[n.Key] : "" + Translated = otherculturedic != null && otherculturedic.ContainsKey(n.Key) ? otherculturedic[n.Key] : string.Empty }; li.Add(p); } diff --git a/Website/Composite/content/misc/editors/resxeditor/resxeditor.css b/Website/Composite/content/misc/editors/resxeditor/resxeditor.css index b61ff86294..04a68f1da1 100644 --- a/Website/Composite/content/misc/editors/resxeditor/resxeditor.css +++ b/Website/Composite/content/misc/editors/resxeditor/resxeditor.css @@ -1,4 +1,4 @@ -@namespace url("http://www.w3.org/1999/xhtml"); +@namespace url("http://www.w3.org/1999/xhtml"); @namespace ui url("http://www.w3.org/1999/xhtml"); html, input, textarea, button, select, td, th { @@ -37,6 +37,7 @@ td { .table td { overflow: visible; color: inherit; + user-select: text; } .table .hidden { From aef1d3cc0010e7957bc0554c655f588eb2e8a02d Mon Sep 17 00:00:00 2001 From: Dmitry Dzygin Date: Thu, 10 Sep 2020 13:59:42 +0200 Subject: [PATCH 13/20] Fix #751 Adding page dialog crashes during position selection if one of the siblings have multiple versions --- .../PageElementProvider/AddNewPageWorkflow.cs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/Composite.Workflows/Plugins/Elements/ElementProviders/PageElementProvider/AddNewPageWorkflow.cs b/Composite.Workflows/Plugins/Elements/ElementProviders/PageElementProvider/AddNewPageWorkflow.cs index c83ee87d1f..2dc9fa389d 100644 --- a/Composite.Workflows/Plugins/Elements/ElementProviders/PageElementProvider/AddNewPageWorkflow.cs +++ b/Composite.Workflows/Plugins/Elements/ElementProviders/PageElementProvider/AddNewPageWorkflow.cs @@ -50,9 +50,8 @@ private Guid GetParentId() return Guid.Empty; } - if (this.EntityToken is DataEntityToken) + if (this.EntityToken is DataEntityToken dataEntityToken) { - DataEntityToken dataEntityToken = (DataEntityToken)this.EntityToken; IPage selectedPage = (IPage)dataEntityToken.Data; return selectedPage.Id; @@ -244,9 +243,8 @@ private void stepInitialize_codeActivity_ExecuteCode(object sender, EventArgs e) { templateId = PageTemplateFacade.GetPageTemplates().Select(t => t.Id).FirstOrDefault(); } - else if (this.EntityToken is DataEntityToken) + else if (this.EntityToken is DataEntityToken dataEntityToken) { - DataEntityToken dataEntityToken = (DataEntityToken)this.EntityToken; IPage selectedPage = (IPage)dataEntityToken.Data; templateId = selectedPage.TemplateId; @@ -420,7 +418,10 @@ private void PrepareStep2_ExecuteCode(object sender, EventArgs e) if (this.GetBinding("SelectedSortOrder") == SortOrder.Relative) { - Dictionary existingPages = PageServices.GetChildren(GetParentId()).ToDictionary(page => page.Id, page => page.Title); + Dictionary existingPages = PageServices.GetChildren(GetParentId()) + .GroupBy(page => page.Id) + .Select(group => group.First()) + .ToDictionary(page => page.Id, page => page.Title); this.Bindings["ExistingPages"] = existingPages; this.Bindings["RelativeSelectedPageId"] = existingPages.First().Key; @@ -476,7 +477,7 @@ private void stepFinalize_codeActivity_ExecuteCode(object sender, EventArgs e) } else { - throw new InvalidOperationException($"Not handled page instert position '{sortOrder}'"); + throw new InvalidOperationException($"Not handled page insert position '{sortOrder}'"); } newPage = newPage.Add(parentId, position); From 246a920a29f1b8edb2285dc5e3e3906898fec0ee Mon Sep 17 00:00:00 2001 From: Dmitry Dzygin Date: Thu, 10 Sep 2020 14:52:36 +0200 Subject: [PATCH 14/20] Making PageRenderer respect PreventFunctionOutputCaching property --- Composite/AspNet/CmsPageHttpHandler.cs | 6 +++- .../WebClient/Renderings/Page/PageRenderer.cs | 32 +++++++++++++++---- 2 files changed, 31 insertions(+), 7 deletions(-) diff --git a/Composite/AspNet/CmsPageHttpHandler.cs b/Composite/AspNet/CmsPageHttpHandler.cs index b920322954..e09b903a51 100644 --- a/Composite/AspNet/CmsPageHttpHandler.cs +++ b/Composite/AspNet/CmsPageHttpHandler.cs @@ -1,6 +1,7 @@ using System.Web; using System.Xml.Linq; using Composite.AspNet.Caching; +using Composite.Core; using Composite.Core.Configuration; using Composite.Core.Instrumentation; using Composite.Core.PageTemplates; @@ -136,7 +137,10 @@ public void ProcessRequest(HttpContext context) // Disabling ASP.NET cache if there's a logged-in user if (consoleUserLoggedIn || preventResponseCaching) { - context.Response.Cache.SetCacheability(HttpCacheability.NoCache); + using (preventResponseCaching ? Profiler.Measure("CmsPageHttpHandler: Disabling HTTP caching as at least one of the functions is not cacheable") : EmptyDisposable.Instance) + { + context.Response.Cache.SetCacheability(HttpCacheability.NoCache); + } } // Inserting performance profiling information diff --git a/Composite/Core/WebClient/Renderings/Page/PageRenderer.cs b/Composite/Core/WebClient/Renderings/Page/PageRenderer.cs index 7b357f9197..1ddfd9ca26 100644 --- a/Composite/Core/WebClient/Renderings/Page/PageRenderer.cs +++ b/Composite/Core/WebClient/Renderings/Page/PageRenderer.cs @@ -340,9 +340,27 @@ public static Control Render(XDocument document, FunctionContextContainer contex { using (TimerProfilerFacade.CreateTimerProfiler()) { + bool disableCaching = false; + using (Profiler.Measure("Executing embedded functions")) { - ExecuteEmbeddedFunctions(document.Root, contextContainer); + ExecuteFunctionsRec(document.Root, contextContainer, func => + { + if (!disableCaching && !FunctionAllowsCaching(func)) + { + disableCaching = true; + } + + return true; + }); + } + + if (disableCaching) + { + using (Profiler.Measure("PageRenderer: Disabling HTTP caching as at least one of the functions is not cacheable")) + { + HttpContext.Current?.Response.Cache.SetCacheability(HttpCacheability.NoCache); + } } using (Profiler.Measure("Resolving page fields")) @@ -653,11 +671,13 @@ public static void ExecuteEmbeddedFunctions(XElement element, FunctionContextCon /// internal static bool ExecuteCacheableFunctions(XElement element, FunctionContextContainer functionContext) { - return ExecuteFunctionsRec(element, functionContext, name => - { - var function = FunctionFacade.GetFunction(name); - return !(function is IDynamicFunction df && df.PreventFunctionOutputCaching); - }); + return ExecuteFunctionsRec(element, functionContext, FunctionAllowsCaching); + } + + private static bool FunctionAllowsCaching(string name) + { + var function = FunctionFacade.GetFunction(name); + return !(function is IDynamicFunction df && df.PreventFunctionOutputCaching); } private static void ReplaceFunctionWithResult(XElement functionCall, object result) From 189613f064a12252c8b45a4ff0c44dd6bd7d502b Mon Sep 17 00:00:00 2001 From: Dmitry Dzygin Date: Wed, 16 Sep 2020 17:26:24 +0200 Subject: [PATCH 15/20] Fixing page object cache functionality in PageRenderer; refactoring for PageObjectCacheFunction --- .../WebClient/Renderings/Page/PageRenderer.cs | 15 +++++- .../Utils/Caching/PageObjectCacheFunction.cs | 49 ++++++++++++------- 2 files changed, 45 insertions(+), 19 deletions(-) diff --git a/Composite/Core/WebClient/Renderings/Page/PageRenderer.cs b/Composite/Core/WebClient/Renderings/Page/PageRenderer.cs index 1ddfd9ca26..44f77c28b6 100644 --- a/Composite/Core/WebClient/Renderings/Page/PageRenderer.cs +++ b/Composite/Core/WebClient/Renderings/Page/PageRenderer.cs @@ -19,6 +19,7 @@ using Composite.Core.Xml; using Composite.C1Console.Security; using Composite.Core.Configuration; +using Composite.Plugins.Functions.FunctionProviders.StandardFunctionProvider.Utils.Caching; using Composite.Plugins.PageTemplates.XmlPageTemplates; namespace Composite.Core.WebClient.Renderings.Page @@ -591,7 +592,7 @@ internal static bool ExecuteFunctionsRec( bool allRecFunctionsExecuted = true; - string functionName = (string) element.Attribute("name"); + string functionName = (string) element.Attribute(XName_Name); object result; try { @@ -601,6 +602,12 @@ internal static bool ExecuteFunctionsRec( bool allParametersEvaluated = true; foreach (XElement parameterNode in parameters.ToList()) { + var parameterName = (string)parameterNode.Attribute(XName_Name); + if (ParameterIsLazyEvaluated(functionName, parameterName)) + { + continue; + } + if (!ExecuteFunctionsRec(parameterNode, functionContext, functionShouldBeExecuted)) { allParametersEvaluated = false; @@ -656,6 +663,12 @@ internal static bool ExecuteFunctionsRec( return allRecFunctionsExecuted; } + private static bool ParameterIsLazyEvaluated(string functionName, string parameterName) + { + return functionName == PageObjectCacheFunction.FunctionName && + parameterName == PageObjectCacheFunction.ParameterNames.ObjectToCache; + } + /// public static void ExecuteEmbeddedFunctions(XElement element, FunctionContextContainer functionContext) { diff --git a/Composite/Plugins/Functions/FunctionProviders/StandardFunctionProvider/Utils/Caching/PageObjectCacheFunction.cs b/Composite/Plugins/Functions/FunctionProviders/StandardFunctionProvider/Utils/Caching/PageObjectCacheFunction.cs index 10b08d72e5..f7222eaf22 100644 --- a/Composite/Plugins/Functions/FunctionProviders/StandardFunctionProvider/Utils/Caching/PageObjectCacheFunction.cs +++ b/Composite/Plugins/Functions/FunctionProviders/StandardFunctionProvider/Utils/Caching/PageObjectCacheFunction.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Collections.Concurrent; using System.Linq; @@ -19,36 +19,49 @@ internal sealed class PageObjectCacheFunction : DowncastableStandardFunctionBase { private static readonly XName FunctionXName = Namespaces.Function10 + "function"; + private const string C1Name = "PageObjectCache"; + private const string C1Namespace = "Composite.Utils.Caching"; + + public static string FunctionName { get; } = $"{C1Namespace}.{C1Name}"; + public PageObjectCacheFunction(EntityTokenFactory entityTokenFactory) - : base("PageObjectCache", "Composite.Utils.Caching", typeof(object), entityTokenFactory) + : base(C1Name, C1Namespace, typeof(object), entityTokenFactory) { } + public class ParameterNames + { + public static readonly string ObjectToCache = nameof(ObjectToCache); + public static readonly string ObjectCacheId = nameof(ObjectCacheId); + public static readonly string SitemapScope = nameof(SitemapScope); + public static readonly string SecondsToCache = nameof(SecondsToCache); + public static readonly string LanguageSpecific = nameof(LanguageSpecific); + } protected override IEnumerable StandardFunctionParameterProfiles { get { WidgetFunctionProvider associationDropDown = StandardWidgetFunctions.DropDownList( - this.GetType(), "PageAssociationRestrictions", "Key", "Value", false, true); + this.GetType(), nameof(PageAssociationRestrictions), "Key", "Value", false, true); - WidgetFunctionProvider textboxWidget = StandardWidgetFunctions.TextBoxWidget; + var textboxWidget = StandardWidgetFunctions.TextBoxWidget; yield return new StandardFunctionParameterProfile( - "ObjectToCache", typeof(object), true, new NoValueValueProvider(), null); + ParameterNames.ObjectToCache, typeof(object), true, new NoValueValueProvider(), null); yield return new StandardFunctionParameterProfile( - "ObjectCacheId", typeof(string), true, new NoValueValueProvider(), textboxWidget); + ParameterNames.ObjectCacheId, typeof(string), true, new NoValueValueProvider(), textboxWidget); yield return new StandardFunctionParameterProfile( - "SitemapScope", + ParameterNames.SitemapScope, typeof(SitemapScope), false, new ConstantValueProvider(SitemapScope.Level1), associationDropDown); yield return new StandardFunctionParameterProfile( - "SecondsToCache", typeof(int), false, new ConstantValueProvider(60), textboxWidget); + ParameterNames.SecondsToCache, typeof(int), false, new ConstantValueProvider(60), textboxWidget); yield return new StandardFunctionParameterProfile( - "LanguageSpecific", + ParameterNames.LanguageSpecific, typeof(bool), false, new ConstantValueProvider(true), @@ -58,13 +71,13 @@ protected override IEnumerable StandardFunctio readonly ConcurrentDictionary _lockCollection = new ConcurrentDictionary(); - bool _poteintialKeysLeakLogged = false; + static bool _potentialKeysLeakLogged = false; public override object Execute(ParameterList parameters, FunctionContextContainer context) { if (DataScopeManager.CurrentDataScope.Name != DataScopeIdentifier.PublicName) { - return parameters.GetParameter("ObjectToCache"); + return parameters.GetParameter(ParameterNames.ObjectToCache); } var cache = HttpRuntime.Cache; @@ -77,22 +90,22 @@ public override object Execute(ParameterList parameters, FunctionContextContaine lock (lockObject) { - if(_lockCollection.Count > 50000 && !_poteintialKeysLeakLogged) + if(_lockCollection.Count > 50000 && !_potentialKeysLeakLogged) { - _poteintialKeysLeakLogged = true; + _potentialKeysLeakLogged = true; Log.LogWarning(nameof(PageObjectCacheFunction), "Potential memory leak in the locks collection"); } result = cache.Get(cacheKey); if (result == null) { - result = parameters.GetParameter("ObjectToCache"); + result = parameters.GetParameter(ParameterNames.ObjectToCache); if (result != null) { result = EvaluateLazyResult(result, context); - int secondsToCache = parameters.GetParameter("SecondsToCache"); + int secondsToCache = parameters.GetParameter(ParameterNames.SecondsToCache); cache.Add( cacheKey, @@ -179,15 +192,15 @@ private static object EvaluateLazyResult(IEnumerable xNodes, FunctionCont private static string BuildCacheKey(ParameterList parameters) { - string cacheKey = parameters.GetParameter("ObjectCacheId"); + string cacheKey = parameters.GetParameter(ParameterNames.ObjectCacheId); - bool languageSpecific = parameters.GetParameter("LanguageSpecific"); + bool languageSpecific = parameters.GetParameter(ParameterNames.LanguageSpecific); if (languageSpecific) { cacheKey = $"{cacheKey}:{Thread.CurrentThread.CurrentCulture}"; } - SitemapScope SitemapScope = parameters.GetParameter("SitemapScope"); + SitemapScope SitemapScope = parameters.GetParameter(ParameterNames.SitemapScope); if (SitemapScope != SitemapScope.All) { Guid associatedPageId = PageStructureInfo.GetAssociatedPageIds(PageRenderer.CurrentPageId, SitemapScope).FirstOrDefault(); From 8faaad6ffa2987138f420ede530ac31f9689916e Mon Sep 17 00:00:00 2001 From: Taras Nakonechnyi Date: Wed, 30 Sep 2020 17:13:02 +0300 Subject: [PATCH 16/20] Fix [Bug] C1 CMS 6.9: {body}#xD; added to TextArea parameter value in function with TextArea parameter #755 --- .../source/top/ui/bindings/data/selectors/SelectorBinding.js | 2 +- .../top/ui/bindings/editors/visualeditor/VisualEditorBinding.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Website/Composite/scripts/source/top/ui/bindings/data/selectors/SelectorBinding.js b/Website/Composite/scripts/source/top/ui/bindings/data/selectors/SelectorBinding.js index 35baaa833d..b6f043746b 100644 --- a/Website/Composite/scripts/source/top/ui/bindings/data/selectors/SelectorBinding.js +++ b/Website/Composite/scripts/source/top/ui/bindings/data/selectors/SelectorBinding.js @@ -875,7 +875,7 @@ SelectorBinding.prototype._applySearchSelection = function () { else { labelBinding = LabelBinding.newInstance(bodyDocument); - labelBinding.setLabel(StringBundle.getString("ui", "AspNetUiControl.Selector.NoMatchesFor").replace("{0}", this._searchString)); + labelBinding.setLabel(StringBundle.getString("ui", "AspNetUiControl.Selector.NoMatchesFor").replace("{0}", this._searchString ? this._searchString.replace(/\$/g,'$$$$') : '')); bodyBinding.add(labelBinding); this._attachSelections(); diff --git a/Website/Composite/scripts/source/top/ui/bindings/editors/visualeditor/VisualEditorBinding.js b/Website/Composite/scripts/source/top/ui/bindings/editors/visualeditor/VisualEditorBinding.js index 222285487f..4c28705bd4 100644 --- a/Website/Composite/scripts/source/top/ui/bindings/editors/visualeditor/VisualEditorBinding.js +++ b/Website/Composite/scripts/source/top/ui/bindings/editors/visualeditor/VisualEditorBinding.js @@ -468,7 +468,7 @@ VisualEditorBinding.prototype.normalizeToDocument = function ( markup ) { var result = markup; if ( !this._isNormalizedDocument ( markup )) { - result = this._getHtmlMarkup().replace("${body}", markup); + result = this._getHtmlMarkup().replace('${body}', markup.replace(/\$/g,'$$$$')); } return result; } From a02d02604cd48bbcd616d1720e00aa2a2d8b6686 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 10 Sep 2020 18:17:15 +0000 Subject: [PATCH 17/20] Bump node-fetch from 1.6.0 to 2.6.1 in /Website Bumps [node-fetch](https://github.com/bitinn/node-fetch) from 1.6.0 to 2.6.1. - [Release notes](https://github.com/bitinn/node-fetch/releases) - [Changelog](https://github.com/node-fetch/node-fetch/blob/master/docs/CHANGELOG.md) - [Commits](https://github.com/bitinn/node-fetch/compare/v1.6.0...v2.6.1) Signed-off-by: dependabot[bot] --- Website/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Website/package.json b/Website/package.json index 53d939be87..530c1c123b 100644 --- a/Website/package.json +++ b/Website/package.json @@ -36,7 +36,7 @@ "load-grunt-tasks": "3.5.0", "mocha": "3.0.1", "nightwatch": "0.9.3", - "node-fetch": "1.6.0", + "node-fetch": "2.6.1", "rimraf": "2.5.4", "robocopy": "^0.1.15", "selenium-server-standalone-jar": "2.53.1", From b6f0e141cbe169fdc27e4d9749c0ad731df44144 Mon Sep 17 00:00:00 2001 From: Dmitry Dzygin Date: Fri, 2 Oct 2020 11:54:36 +0200 Subject: [PATCH 18/20] A more informative exception when user data providers are misconfigured and the same user is registered twice --- Composite/C1Console/Security/UserGroupFacade.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Composite/C1Console/Security/UserGroupFacade.cs b/Composite/C1Console/Security/UserGroupFacade.cs index c5678c9eda..d212832bee 100644 --- a/Composite/C1Console/Security/UserGroupFacade.cs +++ b/Composite/C1Console/Security/UserGroupFacade.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Globalization; using System.Linq; +using Composite.Core.Linq; using Composite.Data; using Composite.Data.Types; @@ -36,7 +37,8 @@ public static IReadOnlyCollection GetUserGroupIds(string username) return _cache.GetOrAdd(username, name => { IUser user = DataFacade.GetData() - .SingleOrDefault(f => string.Compare(f.Username, name, StringComparison.InvariantCultureIgnoreCase) == 0); + .Where(f => string.Compare(f.Username, name, StringComparison.InvariantCultureIgnoreCase) == 0) + .SingleOrDefaultOrException("Multiple data records for the same user name '{0}'", name); Verify.IsNotNull(user, "Failed to find user by name '{0}'", name); From 630477841ad367626c047156cabde8b5999b74be Mon Sep 17 00:00:00 2001 From: Dmitry Dzygin Date: Fri, 2 Oct 2020 11:55:10 +0200 Subject: [PATCH 19/20] Updating version number to 6.10 --- Composite/Properties/SharedAssemblyInfo.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Composite/Properties/SharedAssemblyInfo.cs b/Composite/Properties/SharedAssemblyInfo.cs index b92b89ad53..a6e496d5f4 100644 --- a/Composite/Properties/SharedAssemblyInfo.cs +++ b/Composite/Properties/SharedAssemblyInfo.cs @@ -2,9 +2,9 @@ // General Information about the assemblies Composite and Composite.Workflows #if !InternalBuild -[assembly: AssemblyTitle("C1 CMS 6.9")] +[assembly: AssemblyTitle("C1 CMS 6.10")] #else -[assembly: AssemblyTitle("C1 CMS 6.9 (Internal Build)")] +[assembly: AssemblyTitle("C1 CMS 6.10 (Internal Build)")] #endif [assembly: AssemblyCompany("Orckestra Technologies Inc.")] @@ -13,4 +13,4 @@ [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] -[assembly: AssemblyVersion("6.9.*")] +[assembly: AssemblyVersion("6.10.*")] From 8ca9f26bf26c686b2a6282d4b3a6a1ffceced4da Mon Sep 17 00:00:00 2001 From: Dmitry Dzygin Date: Fri, 2 Oct 2020 13:04:26 +0200 Subject: [PATCH 20/20] Addressing compilation warnings --- .../FileConfigurationSourceImplementation.cs | 2 +- .../Pages/SeoFriendlyRedirectRouteHandler.cs | 8 +++++++ .../PageInternalUrlConverter.cs | 5 +++-- .../Routing/Pages/DefaultPageUrlProvider.cs | 22 +++++++++++++++++-- 4 files changed, 32 insertions(+), 5 deletions(-) diff --git a/Composite/Core/Configuration/FileConfigurationSourceImplementation.cs b/Composite/Core/Configuration/FileConfigurationSourceImplementation.cs index 107358c21b..d8538c7321 100644 --- a/Composite/Core/Configuration/FileConfigurationSourceImplementation.cs +++ b/Composite/Core/Configuration/FileConfigurationSourceImplementation.cs @@ -54,7 +54,7 @@ public override System.Configuration.ConfigurationSection GetSection(string sect { configurationSection = configuration.GetSection(sectionName) as System.Configuration.ConfigurationSection; } - catch (System.Configuration.ConfigurationException ex) + catch { // retry once UpdateCache(); diff --git a/Composite/Core/Routing/Pages/SeoFriendlyRedirectRouteHandler.cs b/Composite/Core/Routing/Pages/SeoFriendlyRedirectRouteHandler.cs index 6abaefd5b3..0d8d5b6b05 100644 --- a/Composite/Core/Routing/Pages/SeoFriendlyRedirectRouteHandler.cs +++ b/Composite/Core/Routing/Pages/SeoFriendlyRedirectRouteHandler.cs @@ -3,15 +3,23 @@ namespace Composite.Core.Routing.Pages { + /// + /// A route handler that performs an HTTP redirect with response code 301 (Permanently moved). + /// public class SeoFriendlyRedirectRouteHandler: IRouteHandler { private readonly string _redirectUrl; + /// + /// Creates a new instance of + /// + /// The URL to redirect to. public SeoFriendlyRedirectRouteHandler(string redirectUrl) { _redirectUrl = redirectUrl; } + /// public IHttpHandler GetHttpHandler(RequestContext requestContext) { return new SeoFriendlyRedirectHttpHandler(_redirectUrl); diff --git a/Composite/Plugins/Routing/InternalUrlConverters/PageInternalUrlConverter.cs b/Composite/Plugins/Routing/InternalUrlConverters/PageInternalUrlConverter.cs index 6cffbb0338..2cbeb377f4 100644 --- a/Composite/Plugins/Routing/InternalUrlConverters/PageInternalUrlConverter.cs +++ b/Composite/Plugins/Routing/InternalUrlConverters/PageInternalUrlConverter.cs @@ -13,9 +13,10 @@ public class PageInternalUrlConverter: IInternalUrlConverter { private static readonly string LogTitle = nameof(PageInternalUrlConverter); - private readonly string[] _acceptedUrlPrefixes = { "page(", "Renderers/Page.aspx" }; + private readonly string[] _acceptedUrlPrefixes = { "page(", "Renderers/Page.aspx" }; - public IEnumerable AcceptedUrlPrefixes { get { return _acceptedUrlPrefixes; } } + /// + public IEnumerable AcceptedUrlPrefixes => _acceptedUrlPrefixes; /// public string ToPublicUrl(string internalPageUrl, UrlSpace urlSpace) diff --git a/Composite/Plugins/Routing/Pages/DefaultPageUrlProvider.cs b/Composite/Plugins/Routing/Pages/DefaultPageUrlProvider.cs index 9b062ef360..ec663c74fa 100644 --- a/Composite/Plugins/Routing/Pages/DefaultPageUrlProvider.cs +++ b/Composite/Plugins/Routing/Pages/DefaultPageUrlProvider.cs @@ -27,7 +27,15 @@ namespace Composite.Plugins.Routing.Pages [ConfigurationElementType(typeof(NonConfigurablePageUrlProvider))] public class DefaultPageUrlProvider: IPageUrlProvider { + /// + /// Url fragment than indicates that the hostname should be ignored when resolving root page. + /// Used for internally by C1 console. + /// public static readonly string UrlMarker_RelativeUrl = "/c1mode(relative)"; + + /// + /// URL fragment that indicates that "unpublished" version of the page should be shown. + /// public static readonly string UrlMarker_Unpublished = "/c1mode(unpublished)"; private static readonly string InternalUrlPrefix = "~/page("; @@ -61,6 +69,9 @@ static DefaultPageUrlProvider() } + /// + /// Creates a new instance of + /// public DefaultPageUrlProvider() { LoadUrlSuffix(); @@ -72,6 +83,8 @@ private static void LoadUrlSuffix() .Select(c => c.PageUrlSuffix).FirstOrDefault() ?? string.Empty; } + + /// [Obsolete] public IPageUrlBuilder CreateUrlBuilder(PublicationScope publicationScope, CultureInfo localizationScope, UrlSpace urlSpace) { @@ -101,6 +114,7 @@ private static IReadOnlyCollection GetHostnameBindings() return result; } + /// public bool IsInternalUrl(string relativeUrl) { string decodedRelativeUrl = HttpUtility.UrlDecode(relativeUrl); @@ -115,10 +129,10 @@ internal static bool IsPageRendererRequest(string filePath) return filePath.EndsWith("Renderers/Page.aspx", true); } + /// public PageUrlData ParseInternalUrl(string relativeUrl) { - UrlKind urlKind; - return ParseInternalUrl(relativeUrl, out urlKind); + return ParseInternalUrl(relativeUrl, out _); } private PageUrlData ParseInternalUrl(string relativeUrl, out UrlKind urlKind) @@ -257,6 +271,7 @@ private static PageUrlData ParseRendererUrl(UrlBuilder urlBuilder) }; } + /// public PageUrlData ParseUrl(string absoluteUrl, out UrlKind urlKind) { Verify.ArgumentNotNullOrEmpty(absoluteUrl, "absoluteUrl"); @@ -305,6 +320,7 @@ private bool IsKnownHostname(string hostname) return GetHostnameBindings().Any(b => b.Hostname == hostname); } + /// public PageUrlData ParseUrl(string relativeUrl, UrlSpace urlSpace, out UrlKind urlKind) { if (IsInternalUrl(relativeUrl)) @@ -565,6 +581,7 @@ private IPage TryGetPageByUrlTitlePath(string pagePath, bool pathInfoExtracted, return currentPage; } + /// protected virtual IPage FindMatchingPage(Guid parentId, string urlTitle) { foreach (var page in GetChildPages(parentId)) @@ -672,6 +689,7 @@ internal static CultureInfo GetCultureInfo(string requestPath, IHostnameBinding return DataLocalizationFacade.DefaultUrlMappingCulture; } + /// public string BuildUrl(PageUrlData pageUrlData, UrlKind urlKind, UrlSpace urlSpace) { Verify.ArgumentCondition(urlKind != UrlKind.Undefined, "urlKind", "Url kind is undefined");