fix #3665 added "Wasabi Hot Storage (EU Central)"

--- Duplicati/Library/Backend/S3/S3Backend.cs
+++ Duplicati/Library/Backend/S3/S3Backend.cs
@@ -17,14 +17,14 @@
 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 //
 #endregion
+using Duplicati.Library.Common.IO;
+using Duplicati.Library.Interface;
 using System;
+using System.Collections.Generic;
 using System.Linq;
-using System.Collections.Generic;
 using System.Text;
 using System.Text.RegularExpressions;
-using Duplicati.Library.Interface;
-using Duplicati.Library.Common.IO;
-
+
 namespace Duplicati.Library.Backend
 {
     public class S3 : IBackend, IStreamingBackend, IRenameEnabledBackend
@@ -48,6 +48,7 @@
             new KeyValuePair<string, string>("IBM COS (S3) Public US", "s3-api.us-geo.objectstorage.softlayer.net"),
             new KeyValuePair<string, string>("Wasabi Hot Storage", "s3.wasabisys.com"),
             new KeyValuePair<string, string>("Wasabi Hot Storage (US West)", "s3.us-west-1.wasabisys.com"),
+            new KeyValuePair<string, string>("Wasabi Hot Storage (EU Central)", "s3.eu-central-1.wasabisys.com"),
         };

         //Updated list: http://docs.amazonwebservices.com/general/latest/gr/rande.html#s3_region
@@ -90,7 +91,8 @@

         public static readonly KeyValuePair<string, string>[] KNOWN_S3_STORAGE_CLASSES;

-        static S3() {
+        static S3()
+        {
             var ns = new List<KeyValuePair<string, string>> {
                 new KeyValuePair<string, string>("(default)", ""),
                 new KeyValuePair<string, string>("Standard", "STANDARD"),
@@ -101,7 +103,7 @@

             try
             {
-                foreach(var sc in ReadStorageClasses())
+                foreach (var sc in ReadStorageClasses())
                     if (!ns.Select(x => x.Value).Contains(sc.Value))
                         ns.Add(sc);
             }
@@ -118,7 +120,7 @@
         /// <returns>The storage classes.</returns>
         private static IEnumerable<KeyValuePair<string, string>> ReadStorageClasses()
         {
-            foreach(var f in typeof(Amazon.S3.S3StorageClass).GetFields(System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.DeclaredOnly | System.Reflection.BindingFlags.Public))
+            foreach (var f in typeof(Amazon.S3.S3StorageClass).GetFields(System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.DeclaredOnly | System.Reflection.BindingFlags.Public))
             {
                 if (f.FieldType == typeof(Amazon.S3.S3StorageClass))
                 {
@@ -133,7 +135,7 @@
         private readonly string m_bucket;
         private readonly string m_prefix;

-        public const string DEFAULT_S3_HOST  = "s3.amazonaws.com";
+        public const string DEFAULT_S3_HOST = "s3.amazonaws.com";
         public const string S3_EU_REGION_NAME = "eu-west-1";
         public const string S3_RRS_CLASS_NAME = "REDUCED_REDUNDANCY";

@@ -151,7 +153,7 @@
         {
             var uri = new Utility.Uri(url);
             uri.RequireHost();
-
+
             string host = uri.Host;
             m_prefix = uri.Path;

@@ -197,13 +199,13 @@

             string s3host;
             options.TryGetValue(SERVER_NAME, out s3host);
-            if (string.IsNullOrEmpty(s3host))
+            if (string.IsNullOrEmpty(s3host))
             {
                 s3host = DEFAULT_S3_HOST;

                 //Change in S3, now requires that you use location specific endpoint
                 if (!string.IsNullOrEmpty(locationConstraint))
-                    foreach(KeyValuePair<string, string> kvp in DEFAULT_S3_LOCATION_BASED_HOSTS)
+                    foreach (KeyValuePair<string, string> kvp in DEFAULT_S3_LOCATION_BASED_HOSTS)
                         if (kvp.Key.Equals(locationConstraint, StringComparison.OrdinalIgnoreCase))
                         {
                             s3host = kvp.Value;
@@ -247,7 +249,7 @@
                         throw new UserInformationException(Strings.S3Backend.UnableToDecodeBucketnameError(url), "S3CannotDecodeBucketName");
                 }

-                Logging.Log.WriteWarningMessage(LOGTAG, "DeprecatedS3Format", null, Strings.S3Backend.DeprecatedUrlFormat("s3://" + m_bucket + "/" + m_prefix));
+                Logging.Log.WriteWarningMessage(LOGTAG, "DeprecatedS3Format", null, Strings.S3Backend.DeprecatedUrlFormat("s3://" + m_bucket + "/" + m_prefix));
             }
             else
             {
@@ -391,7 +393,7 @@
             {
                 StringBuilder hostnames = new StringBuilder();
                 StringBuilder locations = new StringBuilder();
-                foreach(KeyValuePair<string, string> s in KNOWN_S3_PROVIDERS)
+                foreach (KeyValuePair<string, string> s in KNOWN_S3_PROVIDERS)
                     hostnames.AppendLine(string.Format("{0}: {1}", s.Key, s.Value));

                 foreach (KeyValuePair<string, string> s in KNOWN_S3_LOCATIONS)
@@ -399,10 +401,10 @@

                 var defaults = new Amazon.S3.AmazonS3Config();

-                var exts =
+                var exts =
                     typeof(Amazon.S3.AmazonS3Config).GetProperties().Where(x => x.CanRead && x.CanWrite && (x.PropertyType == typeof(string) || x.PropertyType == typeof(bool) || x.PropertyType == typeof(int) || x.PropertyType == typeof(long) || x.PropertyType.IsEnum))
                         .Select(x => (ICommandLineArgument)new CommandLineArgument(
-                            "s3-ext-" + x.Name.ToLowerInvariant(),
+                            "s3-ext-" + x.Name.ToLowerInvariant(),
                             x.PropertyType == typeof(bool) ? CommandLineArgument.ArgumentType.Boolean : x.PropertyType.IsEnum ? CommandLineArgument.ArgumentType.Enumeration : CommandLineArgument.ArgumentType.String,
                             x.Name,
                             string.Format("Extended option {0}", x.Name),


The way Duplicati matches folders against filters, the filter needs to either end with a wildcard or a slash in order to match. However, the 'control_dir_v2', 'Windows.old', and the various Environment.SpecialFolders filters did not have trailing slashes, leading to these filters not actually applying when they should. This change fix the few specific filters that need this specification, and adds a new utiltiy method to produce filters for SpecialFolders in a way that makes sure they end with a trailing slash.

--- Duplicati/Library/Utility/FilterGroups.cs
+++ Duplicati/Library/Utility/FilterGroups.cs
@@ -277,7 +277,7 @@
                 // TODO: The control_dir_v2 might be under a different path for OEM branded instances.
                 // However, the AppName is loaded and controlled by the AutoUpdater assembly, which we can't reference here without an ugly circular dependency or dependency injection.
                 // What is the best way to solve this?
-                yield return FilterGroups.CreateWildcardFilter(@"*/Duplicati/control_dir_v2"); // Duplicati uses this directory to store lock files and communicate with other processes.
+                yield return FilterGroups.CreateWildcardFilter(@"*/Duplicati/control_dir_v2/"); // Duplicati uses this directory to store lock files and communicate with other processes.
                 yield return FilterGroups.CreateWildcardFilter(@"*/Google/Chrome/*cache*");
                 yield return FilterGroups.CreateWildcardFilter(@"*/Google/Chrome/*LOCK*"); // Chrome appears to lock various files under it's settings folder using files named 'LOCK' or 'lockfile'
                 yield return FilterGroups.CreateWildcardFilter(@"*/Google/Chrome/*Current*"); // 'Current Session' and 'Current Tabs' appear to be locked while running Chrome
@@ -335,18 +335,18 @@
                 yield return FilterGroups.CreateWildcardFilter(@"?:/Config.Msi*"); // https://github.com/duplicati/duplicati/issues/2886
                 yield return FilterGroups.CreateWildcardFilter(@"*/Recent/");
                 yield return FilterGroups.CreateWildcardFilter(@"?:/autoexec.bat");
-                yield return Environment.GetFolderPath(Environment.SpecialFolder.System);
-                yield return Environment.GetFolderPath(Environment.SpecialFolder.SystemX86);
-                yield return Environment.GetFolderPath(Environment.SpecialFolder.Windows);
-                yield return Environment.GetFolderPath(Environment.SpecialFolder.Recent);
-
-                var windir = Environment.GetFolderPath(Environment.SpecialFolder.Windows);
+                yield return FilterGroups.CreateSpecialFolderFilter(Environment.SpecialFolder.System);
+                yield return FilterGroups.CreateSpecialFolderFilter(Environment.SpecialFolder.SystemX86);
+                yield return FilterGroups.CreateSpecialFolderFilter(Environment.SpecialFolder.Windows);
+                yield return FilterGroups.CreateSpecialFolderFilter(Environment.SpecialFolder.Recent);
+
+                var windir = FilterGroups.CreateSpecialFolderFilter(Environment.SpecialFolder.Windows);
                 if (!string.IsNullOrWhiteSpace(windir))
                 {
                     yield return windir;

                     // Also exclude "C:\Windows.old\"
-                    yield return windir.TrimEnd('\\', '/') + ".old";
+                    yield return windir.TrimEnd('\\', '/') + ".old" + System.IO.Path.DirectorySeparatorChar;
                 }
             }

@@ -375,9 +375,9 @@
                 yield return FilterGroups.CreateWildcardFilter(@"*/Temporary Internet Files/");
                 yield return FilterGroups.CreateWildcardFilter(@"*/Thumbs.db");

-                yield return Environment.GetFolderPath(Environment.SpecialFolder.History);
-                yield return Environment.GetFolderPath(Environment.SpecialFolder.InternetCache);
-                yield return Environment.GetFolderPath(Environment.SpecialFolder.Cookies);
+                yield return FilterGroups.CreateSpecialFolderFilter(Environment.SpecialFolder.History);
+                yield return FilterGroups.CreateSpecialFolderFilter(Environment.SpecialFolder.InternetCache);
+                yield return FilterGroups.CreateSpecialFolderFilter(Environment.SpecialFolder.Cookies);

             }

@@ -394,12 +394,12 @@

             if (group.HasFlag(FilterGroup.Applications))
             {
-                yield return Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles);
-                yield return Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86);
-                yield return Environment.GetFolderPath(Environment.SpecialFolder.AdminTools);
-                yield return Environment.GetFolderPath(Environment.SpecialFolder.CommonProgramFiles);
-                yield return Environment.GetFolderPath(Environment.SpecialFolder.CommonProgramFilesX86);
-                yield return Environment.GetFolderPath(Environment.SpecialFolder.CommonAdminTools);
+                yield return FilterGroups.CreateSpecialFolderFilter(Environment.SpecialFolder.ProgramFiles);
+                yield return FilterGroups.CreateSpecialFolderFilter(Environment.SpecialFolder.ProgramFilesX86);
+                yield return FilterGroups.CreateSpecialFolderFilter(Environment.SpecialFolder.AdminTools);
+                yield return FilterGroups.CreateSpecialFolderFilter(Environment.SpecialFolder.CommonProgramFiles);
+                yield return FilterGroups.CreateSpecialFolderFilter(Environment.SpecialFolder.CommonProgramFilesX86);
+                yield return FilterGroups.CreateSpecialFolderFilter(Environment.SpecialFolder.CommonAdminTools);
             }
         }

@@ -551,6 +551,35 @@
                 yield return FilterGroups.CreateWildcardFilter(@"/lib64/");
                 yield return FilterGroups.CreateWildcardFilter(@"/opt/");
                 yield return FilterGroups.CreateWildcardFilter(@"/usr/");
+            }
+        }
+
+        /// <summary>
+        /// Creates a filter for a special folder
+        /// </summary>
+        /// <param name="specialFolder">Special folder</param>
+        /// <returns>Special folder filter</returns>
+        private static string CreateSpecialFolderFilter(Environment.SpecialFolder specialFolder)
+        {
+            string folderPath = FilterGroups.CreateSpecialFolderFilter(specialFolder);
+            if (!string.IsNullOrEmpty(folderPath))
+            {
+                string filter = FilterGroups.CreateWildcardFilter(folderPath);
+
+                // Duplicati matches filters against folder paths exactly.
+                // Meaning a filter for 'C:\Windows' won't match 'C:\Windows\'.
+                // So this makes sure special folder's filter's have a trailing directory separator.
+                // (Alternatively, this could append '*' to all folder filters.)
+                if (filter[filter.Length - 1] != System.IO.Path.DirectorySeparatorChar)
+                {
+                    filter += System.IO.Path.DirectorySeparatorChar;
+                }
+
+                return filter;
+            }
+            else
+            {
+                return null;
             }
         }


TwitchListenerClient now implements IClientVisitor


Added callback invocation on FormClosed event


Fix json exception on null paths

Fix json exception on null paths

--- EDDiscovery/EliteDangerous/EDCommander.cs
+++ EDDiscovery/EliteDangerous/EDCommander.cs
@@ -306,11 +306,12 @@
             JObject jo = new JObject();
             foreach (EDCommander cmdr in _commandersDict.Values)
             {
-                jo[cmdr.Name] = new JObject(new
-                {
-                    NetLogDir = cmdr.NetLogDir,
-                    JournalDir = cmdr.JournalDir
-                });
+                JObject j = new JObject();
+                if (cmdr.NetLogDir != null)
+                    jo["NetLogDir"] = cmdr.NetLogDir;
+                if (cmdr.JournalDir != null)
+                    jo["JournalDir"] = cmdr.JournalDir;
+                jo[cmdr.Name] = j;
             }

             using (Stream stream = File.OpenWrite(Path.Combine(EDDConfig.Options.AppDataDirectory, "CommanderPaths.json.tmp")))


tweaking WMI to increase performance with more reuse


Add WriteFile method, add collision handling

--- EmbeddedResource.Plugin/ResourceWriter.cs
+++ EmbeddedResource.Plugin/ResourceWriter.cs
@@ -22,19 +22,38 @@
 		/// <summary>
 		/// Writes all of the embedded resources in a project folder to the target directory
 		/// </summary>
-		/// <param name="sourceDirectory">Path to locate embedded resources in assembly.</param>
+		/// <param name="sourceDirectory">Path to embedded resources in assembly.</param>
+		/// <param name="targetDirectory">Path to folder where resources will be written.</param>
 		/// <param name="recursive">Descend into subdirectories to retrieve resources.
 		/// Recursive writes will treat "." as path delimiters (excluding a final "." for the file extension).
 		/// Non-recursive treats all "." as part of the file name.</param>
 		/// <returns>
 		/// A <see cref="Task"/> which will complete after the folder is written
 		/// </returns>
-		public async Task WriteFolder (string sourceDirectory, string targetDirectory = "", bool recursive = true) {
+		public async Task WriteFolder (string sourceDirectory, string targetDirectory = "", bool recursive = true, CreationCollisionOption option =  CreationCollisionOption.ReplaceExisting) {
 			await WriteFolder (
 				this.Assembly,
 				sourceDirectory,
 				targetDirectory,
-				recursive);
+				recursive,
+				option);
+		}
+
+		/// <summary>
+		/// Writes an embedded resource to the file system
+		/// </summary>
+		/// <param name="assembly"><see cref="Assembly"/> containing embedded resources.</param>
+		/// <param name="source">Relative path to embedded resource in <see cref="Assembly"/>.</param>
+		/// <param name="targetDirectory">Path to folder where resources will be written.</param>
+		/// <returns>
+		/// A <see cref="Task"/> which will complete after the file is written
+		/// </returns>
+		public async Task WriteFile (string source, string targetDirectory = "", CreationCollisionOption option =  CreationCollisionOption.ReplaceExisting) {
+			await WriteFile (
+				this.Assembly,
+				source,
+				targetDirectory,
+				option);
 		}

 		/// <summary>
@@ -45,11 +64,12 @@
 		/// <returns>
 		/// A <see cref="Task"/> which will complete after the file is written
 		/// </returns>
-		public async Task WriteResource (string fileName, string resource) {
+		public async Task WriteResource (string fileName, string resource, CreationCollisionOption option =  CreationCollisionOption.ReplaceExisting) {
 			await WriteResource (
 				this.Assembly,
 				fileName,
-				resource);
+				resource,
+				option);
 		}

@@ -57,14 +77,15 @@
 		/// Writes all of the embedded resources in a project folder to the target directory
 		/// </summary>
 		/// <param name="assembly"><see cref="Assembly"/> containing embedded resources.</param>
-		/// <param name="sourceDirectory">Path to locate embedded resources in assembly.</param>
+		/// <param name="sourceDirectory">Path to embedded resources in assembly.</param>
+		/// <param name="targetDirectory">Path to folder where resources will be written.</param>
 		/// <param name="recursive">Descend into subdirectories to retrieve resources.
 		/// Recursive writes will treat "." as path delimiters (excluding a final "." for the file extension).
 		/// Non-recursive treats all "." as part of the file name.</param>
 		/// <returns>
 		/// A <see cref="Task"/> which will complete after the folder is written
 		/// </returns>
-		public static async Task WriteFolder (Assembly assembly, string sourceDirectory, string targetDirectory = "", bool recursive = true) {
+		public static async Task WriteFolder (Assembly assembly, string sourceDirectory, string targetDirectory = "", bool recursive = true, CreationCollisionOption option =  CreationCollisionOption.ReplaceExisting) {
 			var sourcePrefix = String.Format ("{0}.{1}.",
 				assembly.GetName ().Name,
 				sourceDirectory.Replace (PortablePath.DirectorySeparatorChar, '.'));
@@ -94,8 +115,33 @@
 				await WriteResource (
 					assembly,
 					PortablePath.Combine (targetDirectory, relativePathAndFilename),
-					resource);
+					resource,
+					option);
 			}
+		}
+
+		/// <summary>
+		/// Writes an embedded resource to the file system
+		/// </summary>
+		/// <param name="assembly"><see cref="Assembly"/> containing embedded resources.</param>
+		/// <param name="source">Relative path to embedded resource in <see cref="Assembly"/>.</param>
+		/// <param name="targetDirectory">Path to folder where resources will be written.</param>
+		/// <returns>
+		/// A <see cref="Task"/> which will complete after the file is written
+		/// </returns>
+		public static async Task WriteFile (Assembly assembly, string source, string targetDirectory = "", CreationCollisionOption option =  CreationCollisionOption.ReplaceExisting) {
+			var resource = String.Format ("{0}.{1}",
+				assembly.GetName ().Name,
+				source.Replace (PortablePath.DirectorySeparatorChar, '.'));
+
+			var fileName = source.Substring (
+				source.LastIndexOf (PortablePath.DirectorySeparatorChar) + 1);
+
+			await WriteResource (
+				assembly,
+				PortablePath.Combine(targetDirectory, fileName),
+				resource,
+				option);
 		}

 		/// <summary>
@@ -107,11 +153,8 @@
 		/// <returns>
 		/// A <see cref="Task"/> which will complete after the file is written
 		/// </returns>
-		public static async Task WriteResource (Assembly assembly, string fileName, string resource) {
+		public static async Task WriteResource (Assembly assembly, string fileName, string resource, CreationCollisionOption option =  CreationCollisionOption.ReplaceExisting) {
 			var rootFolder = FileSystem.Current.LocalStorage;
-
-			//			if (await rootFolder.CheckExistsAsync(fileName) == ExistenceCheckResult.FileExists)
-			//				return;

 			var folderName = fileName.Substring (
 				0,
@@ -123,7 +166,7 @@

 			var file = await folder.CreateFileAsync(
 				fileName.Substring(fileName.LastIndexOf (PortablePath.DirectorySeparatorChar) + 1),
-				CreationCollisionOption.ReplaceExisting);
+				option);

 			using (var input = ResourceLoader.GetEmbeddedResourceStream(assembly, resource))
 			using (var output = await file.OpenAsync(FileAccess.ReadAndWrite)) {


Fixed debug shader warnings.

Fixed debug shader warnings.

--- Emotion/Graphics/Shading/ShaderFactory.cs
+++ Emotion/Graphics/Shading/ShaderFactory.cs
@@ -238,12 +238,12 @@
             for (var i = 0; i < source.Length; i++)
             {
                 // Check if version exists.
-                if (source[i][0] != '#')
+                if (i == 0 && source[i][0] != '#')
                 {
                     Engine.Log.Warning($"The first character is the shader is not '#' but is {source[i][0]}.", MessageSource.Debug);
                 }

-                if (!source[i].Contains("#version "))
+                if (i == 0 && !source[i].Contains("#version "))
                 {
                     Engine.Log.Warning($"The shader is missing the version tag.", MessageSource.Debug);
                 }


Upgrades version to 1.1

Upgrades version to 1.1

Because explicit dependencies will change the regime on how .nuget packages are installed

--- SolutionItems/SharedAssemblyInfo.cs
+++ SolutionItems/SharedAssemblyInfo.cs
@@ -1,9 +1,9 @@
 using System.Reflection;

-[assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.1.*")]

 #pragma warning disable CS7035 // The specified version string does not conform to the recommended format - major.minor.build.revision.
 //Disable warning as, it will be in the format stated.

-[assembly: AssemblyFileVersion("1.0.*")]
+[assembly: AssemblyFileVersion("1.1.*")]
 #pragma warning restore CS7035


重构实体转换代码

重构实体转换代码

--- Sources/Ivony.Data/EntityConvert.cs
+++ Sources/Ivony.Data/EntityConvert.cs
@@ -245,12 +245,17 @@

       {
-        var constructors = from i in type.GetConstructors( BindingFlags.Public )
-                           where CheckMethodSignature( i, typeof( DataRow ) ) || CheckMethodSignature( i )
-                           select i;
-
-        if ( constructors.Count() == 1 )
-          return CreateConverter( constructors.First() );
+
+
+
+        var constructor = type.GetConstructor( new[] { typeof( DataRow ) } );
+        if ( constructor != null )
+          return CreateConverter( constructor, true );
+
+        constructor = type.GetConstructor( new Type[0] );
+        if ( constructor != null )
+          return CreateConverter( constructor, false );
+
       }

       throw new NotImplementedException();
@@ -264,19 +269,20 @@

-    private static IEntityConverter<T> CreateConverter( ConstructorInfo constructorInfo )
+    private static IEntityConverter<T> CreateConverter( ConstructorInfo constructorInfo, bool withDataRow )
     {

       Expression body;
-
-      if ( constructorInfo.GetParameters().Length == 1 )
-        body = Expression.New( constructorInfo, Expression.Variable( typeof( DataRow ), "dataItem" ) );
+      var parameter = Expression.Parameter( typeof( DataRow ), "dataItem" );
+
+      if ( withDataRow )
+        body = Expression.New( constructorInfo, parameter );

       else
-        body = Expression.Call( thisFillMethod, Expression.Variable( typeof( DataRow ), "dataItem" ), Expression.New( constructorInfo ) );
-
-
-      var func = Expression.Lambda<Func<DataRow, T>>( body, Expression.Parameter( typeof( DataRow ), "dataItem" ) );
+        body = Expression.Call( thisFillMethod, parameter, Expression.New( constructorInfo ) );
+
+
+      var func = Expression.Lambda<Func<DataRow, T>>( body, parameter );
       return new EntityConverter( func.Compile() );
     }


Add IndicativeTick string message

Add IndicativeTick string message


Fix an issue when circle color resets to default on PIN length change.

--- FormsPinView/FormsPinView.Core/PinView.cs
+++ FormsPinView/FormsPinView.Core/PinView.cs
@@ -395,7 +395,8 @@
                         HeightRequest = 28,
                         WidthRequest = 28,
                         MinimumWidthRequest = 28,
-                        MinimumHeightRequest = 28
+                        MinimumHeightRequest = 28,
+                        Color = Color
                     });
                 }
             }


Compile error fixed

Compile error fixed

--- FurnitureFactory.ConsoleClient/StartUp.cs
+++ FurnitureFactory.ConsoleClient/StartUp.cs
@@ -6,6 +6,7 @@
     using FurnitureFactory.Data.Json;
     using FurnitureFactory.Data.Xml.Exporters;
     using FurnitureFactory.Logic.DataImporters;
+    using FurnitureFactory.Logic.Exporters;

     public class StartUp
     {


Fixes a bug where billboarded text was displayed upside down

--- GizmoTurtle/Assets/Scripts/GizmoTurtle.cs
+++ GizmoTurtle/Assets/Scripts/GizmoTurtle.cs
@@ -46,6 +46,10 @@

     public bool UpsideDown()
     {
+        if (forward == Camera.current.transform.right)
+        {
+            return false;
+        }
         return Vector3.Dot(-this.right, Vector3.up) > 0;
     }


Fix a few errors

Fix a few errors

--- IntranetGJAK/Controllers/UploadHandler.cs
+++ IntranetGJAK/Controllers/UploadHandler.cs
@@ -96,10 +96,10 @@

         //DONT USE THIS IF YOU NEED TO ALLOW LARGE FILES UPLOADS
         //Credit to i-e-b and his ASP.Net uploader for the bulk of the upload helper methods - https://github.com/i-e-b/jQueryFileUpload.Net
-        private void UploadPartialFile(string fileName, HttpRequestBase request, List<ViewDataUploadFilesResult> statuses)
+        private void UploadPartialFile(string fileName, HttpRequest request, List<ViewDataUploadFilesResult> statuses)
         {
-            if (request.Files.Count != 1) throw new HttpRequestValidationException("Attempt to upload chunked file containing more than one fragment per request");
-            var file = request.Files[0];
+            if (request.Form.Files.Count != 1) throw new HttpRequestValidationException("Attempt to upload chunked file containing more than one fragment per request");
+            var file = request.Form.Files[0];
             var inputStream = file.InputStream;

             var fullName = Path.Combine(StorageRoot, Path.GetFileName(fileName));
@@ -131,11 +131,11 @@

         //DONT USE THIS IF YOU NEED TO ALLOW LARGE FILES UPLOADS
         //Credit to i-e-b and his ASP.Net uploader for the bulk of the upload helper methods - https://github.com/i-e-b/jQueryFileUpload.Net
-        private void UploadWholeFile(HttpRequestBase request, List<ViewDataUploadFilesResult> statuses)
+        private void UploadWholeFile(HttpRequest request, List<ViewDataUploadFilesResult> statuses)
         {
-            for (int i = 0; i < request.Files.Count; i++)
+            for (int i = 0; i < request.Form.Files.Count; i++)
             {
-                var file = request.Files[i];
+                var file = request.Form.Files[i];

                 var fullPath = Path.Combine(StorageRoot, Path.GetFileName(file.FileName));


Brought settings file back in

Brought settings file back in

--- aaMQTT/Program.cs
+++ aaMQTT/Program.cs
@@ -10,7 +10,6 @@
 using aaMXItem;

 [assembly: log4net.Config.XmlConfigurator(ConfigFile = "log.config", Watch = true)]
-
 namespace aaMQTT
 {
     class Program


Calling Split only once. (#5107)

Calling Split only once. (#5107)

--- samples/dotnet-runtimeinfo/Program.cs
+++ samples/dotnet-runtimeinfo/Program.cs
@@ -14,9 +14,10 @@
             WriteLine("**.NET information");
             WriteLine($"{nameof(Environment.Version)}: {Environment.Version}");
             WriteLine($"{nameof(RuntimeInformation.FrameworkDescription)}: {RuntimeInformation.FrameworkDescription}");
-            var assemblyInformation = ((AssemblyInformationalVersionAttribute[])typeof(object).Assembly.GetCustomAttributes(typeof(AssemblyInformationalVersionAttribute),false))[0];
-            WriteLine($"Libraries version: {assemblyInformation.InformationalVersion.Split('+')[0]}");
-            WriteLine($"Libraries hash: {assemblyInformation.InformationalVersion.Split('+')[1]}");
+            var assemblyInformation = ((AssemblyInformationalVersionAttribute[])typeof(object).Assembly.GetCustomAttributes(typeof(AssemblyInformationalVersionAttribute), false))[0];
+            var informationalVersionSplit = assemblyInformation.InformationalVersion.Split('+');
+            WriteLine($"Libraries version: {informationalVersionSplit[0]}");
+            WriteLine($"Libraries hash: {informationalVersionSplit[1]}");
             WriteLine();
             WriteLine("**Environment information");
             WriteLine($"{nameof(RuntimeInformation.OSDescription)}: {RuntimeInformation.OSDescription}");


移除关于资源文件的单元测试。

移除关于资源文件的单元测试。

--- test/Util.Tests/Datas/Queries/QueryTest.cs
+++ test/Util.Tests/Datas/Queries/QueryTest.cs
@@ -110,12 +110,12 @@
         /// <summary>
         /// 测试添加查询条件 - 同时添加2个查询条件，抛出异常
         /// </summary>
-        [Fact]
-        public void TestWhereIfNotEmpty_2Condition_Throw() {
-            AssertHelper.Throws<InvalidOperationException>( () => {
-                _query.WhereIfNotEmpty( t => t.Name == "A" && t.Tel == 1 );
-            }, string.Format( LibraryResource.OnlyOnePredicate, "t => ((t.Name == \"A\") AndAlso (t.Tel == 1))" ) );
-        }
+        //[Fact]
+        //public void TestWhereIfNotEmpty_2Condition_Throw() {
+        //    AssertHelper.Throws<InvalidOperationException>( () => {
+        //        _query.WhereIfNotEmpty( t => t.Name == "A" && t.Tel == 1 );
+        //    }, string.Format( LibraryResource.OnlyOnePredicate, "t => ((t.Name == \"A\") AndAlso (t.Tel == 1))" ) );
+        //}

         /// <summary>
         /// 添加范围查询条件 - 整型


fixed a bug about sending message

fixed a bug about sending message

git-svn-id: 264d93eb9283b6a3bfed80dd266caeb8f93e7ca3@72663 81fbe566-5dc4-48c1-bdea-7421811ca204

--- mainline/SuperWebSocket/WebSocketSession.cs
+++ mainline/SuperWebSocket/WebSocketSession.cs
@@ -169,10 +169,7 @@
         {
             lock (m_SendingQueue)
             {
-                for (var i = 0; i < data.Count; i++)
-                {
-                    m_SendingQueue.Enqueue(data);
-                }
+                m_SendingQueue.Enqueue(data);
             }

             DequeueSend();


Add configurable assembly filter

Add configurable assembly filter

Fixes https://github.com/aardvark-platform/aardvark.base/issues/52

--- src/Aardvark.Base/Introspection/Introspection.cs
+++ src/Aardvark.Base/Introspection/Introspection.cs
@@ -127,6 +127,85 @@
             ? Path.GetDirectoryName(CurrentEntryAssembly.Location)
             : null
             ;
+
+        private static HashSet<string> s_defaultAssemblyBlacklist =
+            new HashSet<string>(
+                new[]
+                {
+                    "Aardvark.Unmanaged",
+                    "Aardvark.Unmanaged.Diagnostics",
+                    "ASift.CLI",
+                    "CUDA.NET",
+                    "Emgu.CV",
+                    "FreeImageNET",
+                    "levmarCLI",
+                    "LSDCli",
+                    "OpenNI.Net",
+                    "OpenSURFcs",
+                    "PdfSharp",
+                    "PresentationFramework",
+                    "PresentationCore",
+                    "PrimitiveShapesCLI",
+                    "SiftGpu.CLI",
+                    "SlamToroCli",
+                    "System",
+                    "WindowsBase",
+                    "OpenTK"
+                }
+            );
+
+        private static bool DefaultAssemblyFilter(string name)
+        {
+            return !(s_defaultAssemblyBlacklist.Contains(name) ||
+                     name.StartsWith("System.") ||
+                     name.StartsWith("VRLib.CLI") ||
+                     name.StartsWith("Microsoft") ||
+                     name.StartsWith("LidorSystems") ||
+                     name.StartsWith("WeifenLuo.") ||
+                     name.StartsWith("OpenCV") ||
+                     name.StartsWith("nunit.") ||
+                     name.StartsWith("Extreme.Numerics") ||
+                     name.StartsWith("fftwlib") ||
+                     name.StartsWith("GraphCutsCLI") ||
+                     name.StartsWith("Interop.MLApp") ||
+                     name.StartsWith("IPP") ||
+                     name.StartsWith("IronRuby") ||
+                     name.StartsWith("MapTools") ||
+                     name.StartsWith("MetaDataExtractor") ||
+                     name.StartsWith("mscorlib") ||
+                     name.StartsWith("SlimDX") ||
+                     name.StartsWith("TDx.TDxInput") ||
+                     name.StartsWith("WiimoteLib") ||
+                     name.StartsWith("OpenTK") ||
+                     name.StartsWith("Kitware") ||
+                     name.StartsWith("ICSharpCode") ||
+                     name.StartsWith("WindowsFormsIntegration") ||
+                     name.StartsWith("Roslyn") ||
+                     name.StartsWith("SharpDX") ||
+                     name.StartsWith("Aardvark.Jynx.Native") ||
+                     name.StartsWith("SurfaceQueueInteropHelper") ||
+                     name.StartsWith("ScintillaNET") ||
+                     name.StartsWith("IKVM") ||
+                     name.StartsWith("Super") ||
+                     name.StartsWith("Java") ||
+                     name.StartsWith("OpenTK"));
+        }
+
+        /// <summary>
+        /// Determines if the built-in assembly filter is ignored or applied (default: false).
+        /// </summary>
+        public static bool IgnoreDefaultAssemblyFilter { get; set; } = false;
+
+        /// <summary>
+        /// Filter function determining if an assembly with the given name (without extension) should be loaded or ignored.
+        /// <see cref="IgnoreDefaultAssemblyFilter"/> determines if the function is applied on top of the built-in filter rules.
+        /// </summary>
+        public static Func<string, bool> CustomAssemblyFilter { get; set; } = (_) => true;
+
+        internal static bool AssemblyFilter(string name)
+        {
+            return (IgnoreDefaultAssemblyFilter || DefaultAssemblyFilter(name)) && CustomAssemblyFilter(name);
+        }
     }

     public static class Introspection
@@ -277,32 +356,7 @@

         static Introspection()
         {
-            // (1) initializing s_assembliesThatFailedToLoad
-            new []
-            {
-                "Aardvark.Unmanaged",
-                "Aardvark.Unmanaged.Diagnostics",
-                "ASift.CLI",
-                "CUDA.NET",
-                "Emgu.CV",
-                "FreeImageNET",
-                "levmarCLI",
-                "LSDCli",
-                "OpenNI.Net",
-                "OpenSURFcs",
-                "PdfSharp",
-                "PresentationFramework",
-                "PresentationCore",
-                "PrimitiveShapesCLI",
-                "SiftGpu.CLI",
-                "SlamToroCli",
-                "System",
-                "WindowsBase",
-                "OpenTK"
-            }
-            .ForEach(x => s_assembliesThatFailedToLoad.Add(x));
-
-            // (2) enumerating all assemblies reachable from entry assembly
+            // enumerating all assemblies reachable from entry assembly
             s_assemblies = new Dictionary<string, Assembly>();
             var entryAssembly = Assembly.GetEntryAssembly();

@@ -396,42 +450,10 @@
             if (string.IsNullOrEmpty(name)) return;
             if (s_assembliesThatFailedToLoad.Contains(name)) return;
             if (s_assemblies.ContainsKey(name)) return;
-
-            if (name.StartsWith("System.") ||
-                name.StartsWith("VRLib.CLI") ||
-                name.StartsWith("Microsoft") ||
-                name.StartsWith("LidorSystems") ||
-                name.StartsWith("WeifenLuo.") ||
-                name.StartsWith("OpenCV") ||
-                name.StartsWith("nunit.") ||
-                name.StartsWith("Extreme.Numerics") ||
-                name.StartsWith("fftwlib") ||
-                name.StartsWith("GraphCutsCLI") ||
-                name.StartsWith("Interop.MLApp") ||
-                name.StartsWith("IPP") ||
-                name.StartsWith("IronRuby") ||
-                name.StartsWith("MapTools") ||
-                name.StartsWith("MetaDataExtractor") ||
-                name.StartsWith("mscorlib") ||
-                name.StartsWith("SlimDX") ||
-                name.StartsWith("TDx.TDxInput") ||
-                name.StartsWith("WiimoteLib") ||
-                name.StartsWith("OpenTK") ||
-                name.StartsWith("Kitware") ||
-                name.StartsWith("ICSharpCode") ||
-                name.StartsWith("WindowsFormsIntegration") ||
-                name.StartsWith("Roslyn") ||
-                name.StartsWith("SharpDX") ||
-                name.StartsWith("Aardvark.Jynx.Native") ||
-                name.StartsWith("SurfaceQueueInteropHelper") ||
-                name.StartsWith("ScintillaNET") ||
-                name.StartsWith("IKVM") ||
-                name.StartsWith("Super") ||
-                name.StartsWith("Java") ||
-                name.StartsWith("OpenTK")
-                )
-            {
-                s_assembliesThatFailedToLoad.Add(name);
+
+            if (!IntrospectionProperties.AssemblyFilter(name))
+            {
+                Report.Line(4, "[Introspection] Ignoring assembly {0} due to filter", name);
                 return;
             }

@@ -793,11 +815,18 @@
             var cache = ReadCacheFile();
             var newCache = new Dictionary<string, Tuple<DateTime, bool>>();

-            var folder = IntrospectionProperties.CurrentEntryPath;
-            var assemblies = Directory.EnumerateFiles(folder)
-                                      .Where(p => { var ext = Path.GetExtension(p).ToLowerInvariant(); return ext == ".dll" || ext == ".exe"; })
-                                      .Select(Path.GetFullPath)
-                                      .ToArray();
+            var folder = IntrospectionProperties.CurrentEntryPath;
+            string[] assemblies =
+                    Directory.EnumerateFiles(folder)
+                        .Where(p => { var ext = Path.GetExtension(p).ToLowerInvariant(); return ext == ".dll" || ext == ".exe"; })
+                        .Where(p => {
+                            var name = Path.GetFileNameWithoutExtension(p);
+                            var f = IntrospectionProperties.AssemblyFilter(name);
+                            if (!f) { Report.Line(4, "[GetPluginAssemblyPaths] Ignoring assembly {0} due to filter", name); }
+                            return f;
+                        })
+                        .Select(Path.GetFullPath)
+                        .ToArray();

             var paths = new List<string>();


marked NativeLibraryPath obsolete since each library now (> 5.1) gets its own native library unpack path

--- src/Aardvark.Base/Introspection/Introspection.cs
+++ src/Aardvark.Base/Introspection/Introspection.cs
@@ -1353,6 +1353,7 @@
         ///
         /// Setting NativeLibraryPath to the application path and SeparateLibraryDirectories to false will result in the "old" style of
         /// loading native libs, that is not compatible if the application does not have write permission to the directory (e.g. ProgramFiles).
+        [Obsolete("using explicit native library path is no longer possble https://github.com/aardvark-platform/aardvark.base/issues/64")]
         public static string NativeLibraryPath = Path.Combine(Path.GetTempPath(), "aardvark-native");

         /// Specify if native libraries should be extracted each to its own sub folder or directory to the NativeLibraryPath


Updates to new UsableObject interface method

--- src/Assets/CommonScripts/Inventory/ObjectSlot.cs
+++ src/Assets/CommonScripts/Inventory/ObjectSlot.cs
@@ -1,5 +1,6 @@
 using UnityEngine;
 using System.Collections.Generic;
+using System;

 public class ObjectSlot : MonoBehaviour, UsableObject {

@@ -10,6 +11,10 @@
     public string GetTooltip()
     {
         return string.Format("This object is looking for {0}", triggerObjectName);
+    }
+
+    public void Nearby(GameObject user)
+    {
     }

     public void Use(GameObject user)


BL-10704 Undetected Disconnected state

BL-10704 Undetected Disconnected state

* on Checkin we now check connection just after checkin
   in case the Internet failed while checking in

--- src/BloomExe/TeamCollection/TeamCollectionApi.cs
+++ src/BloomExe/TeamCollection/TeamCollectionApi.cs
@@ -513,8 +513,6 @@
 					BookHistory.AddEvent(_bookSelection.CurrentSelection, BookHistoryEventType.CheckIn, message);
 					BookHistory.SetPendingCheckinMessage(_bookSelection.CurrentSelection, "");

-					_tcManager.CurrentCollection.PutBook(_bookSelection.CurrentSelection.FolderPath, checkin:true);
-
 					Analytics.Track("TeamCollectionCheckinBook",
 						new Dictionary<string, string>(){
 							{"CollectionId", _settings?.CollectionId},
@@ -548,9 +546,10 @@
 							{"BookName", _bookSelection?.CurrentSelection?.Title}
 						});
 				}
-
 				UpdateUiForBook();
 				request.PostSucceeded();
+
+				Application.Idle += OnIdleConnectionCheck;
 			}
 			catch (Exception e)
 			{
@@ -567,6 +566,18 @@
 				NonFatalProblem.ReportSentryOnly(e, $"Something went wrong for {request.LocalPath()} ({_bookSelection?.CurrentSelection?.FolderPath})");
 				request.Failed("checkin failed");
 			}
+		}
+
+		private void OnIdleConnectionCheck(object sender, EventArgs e)
+		{
+			Application.Idle -= OnIdleConnectionCheck;
+
+			// BL-10704: In case the Internet went away while we were trying to CheckIn a book...
+			// This will at least signal to the user in the Dropbox case, that while his checkin
+			// may have succeeded, his colleagues won't know about it until the Internet is up again.
+			// If we don't do it "OnIdle", the book status pane doesn't reflect that we actually did
+			// (probably, assuming we are on Dropbox, anyway) complete the checkin.
+			_tcManager.CheckConnection();
 		}

 		// Tell the CollectionSettingsDialog that we should reopen the collection now


Added missing documentation to the Dictionary extensions

--- src/DevOpsFlex.Messaging/DictionaryExtensions.cs
+++ src/DevOpsFlex.Messaging/DictionaryExtensions.cs
@@ -4,16 +4,17 @@
     using System.Collections.Generic;

     /// <summary>
-    ///
+    /// Contains extensions to <see cref="System.Collections.IDictionary"/>.
     /// </summary>
     public static class DictionaryExtensions
     {
         /// <summary>
-        ///
+        /// Releases a <see cref="IDictionary{TKey,TValue}"/> where <typeparamref name="TValue"/> implements <see cref="IDisposable"/> by calling
+        /// Dispose() on all it's values and then clearing it.
         /// </summary>
-        /// <param name="source"></param>
-        /// <typeparam name="TKey"></typeparam>
-        /// <typeparam name="TValue"></typeparam>
+        /// <param name="source">The source <see cref="IDictionary{TKey,TValue}"/> that we want to release.</param>
+        /// <typeparam name="TKey">The type of the keys in the dictionary.</typeparam>
+        /// <typeparam name="TValue">The type of the values in the dictionary.</typeparam>
         public static void Release<TKey, TValue>(this IDictionary<TKey, TValue> source)
             where TValue : IDisposable
         {


Makes 'ChloeOracleConnection' cloneable.

--- src/DotNet/Chloe.Oracle/ChloeOracleConnection.cs
+++ src/DotNet/Chloe.Oracle/ChloeOracleConnection.cs
@@ -6,7 +6,7 @@

 namespace Chloe.Oracle
 {
-    public class ChloeOracleConnection : IDbConnection, IDisposable
+    public class ChloeOracleConnection : IDbConnection, IDisposable, ICloneable
     {
         IDbConnection _dbConnection;
         public ChloeOracleConnection(IDbConnection dbConnection)
@@ -62,5 +62,14 @@
         {
             this._dbConnection.Dispose();
         }
+        public object Clone()
+        {
+            if (this._dbConnection is ICloneable)
+            {
+                return new ChloeOracleConnection((IDbConnection)((ICloneable)this._dbConnection).Clone());
+            }
+
+            throw new NotSupportedException();
+        }
     }
 }


Fix merge conflict issue.

Fix merge conflict issue.

--- src/EncompassRest/Webhook/WebhookSubscription.cs
+++ src/EncompassRest/Webhook/WebhookSubscription.cs
@@ -13,7 +13,7 @@
         private string _endpoint;
         private string _subscriptionId;
         private StringEnumValue<WebhookResourceType> _resource;
-        private IList<StringEnumValue<WebhookResourceEvent>> _events;
+        private List<StringEnumValue<WebhookResourceEvent>> _events;
         private WebhookFilters _filters;
         private NeverSerializeValue<string> _objectUrn;
         private NeverSerializeValue<string> _clientId;


Updated to company policy re: grace period.

--- src/FPS/ViewModels/Timekeeping/TimeAttendance.cs
+++ src/FPS/ViewModels/Timekeeping/TimeAttendance.cs
@@ -78,6 +78,13 @@

             var remarks = new List<string>();

+            // update to grace period re: updated company policy
+            // starting march 15, 2016
+            if (date >= new DateTime(2016, 3, 15) && IsWeekDay(date))
+            {
+                gracePeriod = new TimeSpan(0, 20, 0);
+            }
+
             // 1 hour late if no timein
             if (TimeIn == null)
             {


Normalised line endings.

Normalised line endings.

--- src/FluentNHibernate/Mapping/ClasslikeMapBase.cs
+++ src/FluentNHibernate/Mapping/ClasslikeMapBase.cs
@@ -335,7 +335,7 @@
         /// <example>
         /// HasMany(x => x.Locations);
         /// </example>
-        public MapBuilder<TKey, TValue> HasMany<TKey, TValue>(Expression<Func<T, IDictionary<TKey, TValue>>> memberExpression)
+        public MapBuilder<TKey, TValue> HasMany<TKey, TValue>(Expression<Func<T, IDictionary<TKey, TValue>>> memberExpression)
         {
             return HasManyMap(memberExpression);
         }


Fix whitespace formatting

Fix whitespace formatting

--- src/GitVersion.LibGit2Sharp/Git/GitRepository.cs
+++ src/GitVersion.LibGit2Sharp/Git/GitRepository.cs
@@ -81,8 +81,8 @@
                     catch (Exception)
                     {
                         return int.MaxValue; // this should be somewhat puzzling to see,
-                                             // so we may have reached our goal to show that
-                                             // that repo is really "Dirty"...
+                        // so we may have reached our goal to show that
+                        // that repo is really "Dirty"...
                     }
                 }

@@ -184,7 +184,7 @@
         }
         public void Checkout(string commitOrBranchSpec)
         {
-            new OperationWithExponentialBackoff<LockedFileException>(new ThreadSleep(), log, () => Commands.Checkout(repositoryInstance, commitOrBranchSpec) , 6).ExecuteAsync().Wait();
+            new OperationWithExponentialBackoff<LockedFileException>(new ThreadSleep(), log, () => Commands.Checkout(repositoryInstance, commitOrBranchSpec), 6).ExecuteAsync().Wait();
         }

         public void Fetch(string remote, IEnumerable<string> refSpecs, AuthenticationInfo auth, string logMessage)


add better version info for home page

add better version info for home page

--- src/IdentityServer4/host/Views/Home/Index.cshtml
+++ src/IdentityServer4/host/Views/Home/Index.cshtml
@@ -1,5 +1,8 @@
+@using System.Reflection
+@using System.Diagnostics
+
 @{
-    var version = typeof(IdentityServer4.Hosting.IdentityServerMiddleware).Assembly.GetName().Version.ToString();
+    var version = FileVersionInfo.GetVersionInfo(typeof(IdentityServer4.Hosting.IdentityServerMiddleware).Assembly.Location).ProductVersion.Split('+').First();
 }

 <div class="welcome-page">


LWDEV-3559: disabled new limit orders creation is day off

--- src/MarginTrading.Services/MarketMakerService.cs
+++ src/MarginTrading.Services/MarketMakerService.cs
@@ -10,16 +10,21 @@
     public class MarketMakerService : IFeedConsumer
     {
         private readonly IInternalMatchingEngine _matchingEngine;
+        private readonly AssetPairDayOffService _assetPairDayOffService;

-        public MarketMakerService(IInternalMatchingEngine matchingEngine)
+        public MarketMakerService(IInternalMatchingEngine matchingEngine, AssetPairDayOffService assetPairDayOffService)
         {
             _matchingEngine = matchingEngine;
+            _assetPairDayOffService = assetPairDayOffService;
         }

         public void ConsumeFeed(MarketMakerOrderCommandsBatchMessage batch)
         {
             batch.AssetPairId.RequiredNotNullOrWhiteSpace(nameof(batch.AssetPairId));
             batch.Commands.RequiredNotNull(nameof(batch.Commands));
+
+            if (_assetPairDayOffService.IsDayOff(batch.AssetPairId))
+                return;

             var model = new SetOrderModel {MarketMakerId = batch.MarketMakerId};


Add LogInfo when git not found (#2092)

Add LogInfo when git not found (#2092)

* Add LogInfo when git not found

* Update GitUtility.cs

--- src/Microsoft.DocAsCode.Common/Git/GitUtility.cs
+++ src/Microsoft.DocAsCode.Common/Git/GitUtility.cs
@@ -1,303 +1,303 @@
-// Copyright (c) Microsoft. All rights reserved.
-// Licensed under the MIT license. See LICENSE file in the project root for full license information.
-
-namespace Microsoft.DocAsCode.Common.Git
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+namespace Microsoft.DocAsCode.Common.Git
 {
-    using System;
-    using System.Collections.Concurrent;
-    using System.Diagnostics;
-    using System.IO;
-    using System.Text;
-
+    using System;
+    using System.Collections.Concurrent;
+    using System.Diagnostics;
+    using System.IO;
+    using System.Text;
+
     using Microsoft.DocAsCode.Plugins;
-
-    public static class GitUtility
-    {
-        private static readonly string CommandName = "git";
-        private static readonly int GitTimeOut = 1000;
-
-        private static readonly string GetRepoRootCommand = "rev-parse --show-toplevel";
-        private static readonly string GetLocalBranchCommand = "rev-parse --abbrev-ref HEAD";
-        private static readonly string GetLocalBranchCommitIdCommand = "rev-parse HEAD";
-        private static readonly string GetRemoteBranchCommand = "rev-parse --abbrev-ref @{u}";
-        private static readonly string GetDeletedFileContentCommand = "show {0}^:{1}";
-        private static readonly string GetFileLastCommitIdCommand = "rev-list --max-count=1 --all -- {0}";
-
-        // TODO: only get default remote's url currently.
-        private static readonly string GetOriginUrlCommand = "config --get remote.origin.url";
-
-        private static readonly string[] BuildSystemBranchName = new[]
-        {
-            "APPVEYOR_REPO_BRANCH",   // AppVeyor
-            "Git_Branch",             // Team City
-            "CI_BUILD_REF_NAME",      // GitLab CI
-            "GIT_LOCAL_BRANCH",       // Jenkins
-            "GIT_BRANCH",             // Jenkins
-            "BUILD_SOURCEBRANCHNAME"  // VSO Agent
-        };
-
-        private static readonly ConcurrentDictionary<string, GitRepoInfo> Cache = new ConcurrentDictionary<string, GitRepoInfo>();
-
-        private static bool? GitCommandExists = null;
-        private static object SyncRoot = new object();
-
-        public static GitDetail TryGetFileDetail(string filePath)
-        {
-            GitDetail detail = null;
-            try
-            {
-                detail = GetFileDetail(filePath);
-            }
-            catch (Exception)
-            {
-                // ignored
-            }
-            return detail;
-        }
-
-        public static GitDetail GetFileDetail(string filePath)
-        {
-            if (string.IsNullOrEmpty(filePath) || !ExistGitCommand())
-            {
-                return null;
-            }
-
-            var path = Path.Combine(EnvironmentContext.BaseDirectory, filePath).ToNormalizedPath();
-
-            var detail = GetFileDetailCore(path);
-            return detail;
-        }
-
-        public static GitRepoInfo GetRepoInfo(string directory)
-        {
-            if (directory == null)
+
+    public static class GitUtility
+    {
+        private static readonly string CommandName = "git";
+        private static readonly int GitTimeOut = 1000;
+
+        private static readonly string GetRepoRootCommand = "rev-parse --show-toplevel";
+        private static readonly string GetLocalBranchCommand = "rev-parse --abbrev-ref HEAD";
+        private static readonly string GetLocalBranchCommitIdCommand = "rev-parse HEAD";
+        private static readonly string GetRemoteBranchCommand = "rev-parse --abbrev-ref @{u}";
+        private static readonly string GetDeletedFileContentCommand = "show {0}^:{1}";
+        private static readonly string GetFileLastCommitIdCommand = "rev-list --max-count=1 --all -- {0}";
+
+        // TODO: only get default remote's url currently.
+        private static readonly string GetOriginUrlCommand = "config --get remote.origin.url";
+
+        private static readonly string[] BuildSystemBranchName = new[]
+        {
+            "APPVEYOR_REPO_BRANCH",   // AppVeyor
+            "Git_Branch",             // Team City
+            "CI_BUILD_REF_NAME",      // GitLab CI
+            "GIT_LOCAL_BRANCH",       // Jenkins
+            "GIT_BRANCH",             // Jenkins
+            "BUILD_SOURCEBRANCHNAME"  // VSO Agent
+        };
+
+        private static readonly ConcurrentDictionary<string, GitRepoInfo> Cache = new ConcurrentDictionary<string, GitRepoInfo>();
+
+        private static bool? GitCommandExists = null;
+        private static object SyncRoot = new object();
+
+        public static GitDetail TryGetFileDetail(string filePath)
+        {
+            GitDetail detail = null;
+            try
+            {
+                detail = GetFileDetail(filePath);
+            }
+            catch (Exception)
+            {
+                // ignored
+            }
+            return detail;
+        }
+
+        public static GitDetail GetFileDetail(string filePath)
+        {
+            if (string.IsNullOrEmpty(filePath) || !ExistGitCommand())
             {
                 return null;
-            }
-
-            if (IsGitRoot(directory))
-            {
-                return Cache.GetOrAdd(directory, GetRepoInfoCore);
-            }
-
-            var parentDirInfo = Directory.GetParent(directory);
-            if (parentDirInfo == null)
-            {
-                return null;
-            }
-
-            return Cache.GetOrAdd(directory, d => GetRepoInfo(parentDirInfo.FullName));
-        }
-
-        #region Private Methods
-
-        private static bool IsGitRoot(string directory)
-        {
-            var gitPath = Path.Combine(directory, ".git");
-
-            // git submodule contains only a .git file instead of a .git folder
-            return Directory.Exists(gitPath) || File.Exists(gitPath);
-        }
-
-        private static GitDetail GetFileDetailCore(string filePath)
-        {
-            string directory;
-            if (PathUtility.IsDirectory(filePath))
-            {
-                directory = filePath;
-            }
-            else
-            {
-                directory = Path.GetDirectoryName(filePath);
-            }
-
-            var repoInfo = Cache.GetOrAdd(directory, GetRepoInfo);
-
-            return new GitDetail
-            {
-                // TODO: remove commit id to avoid config hash changed
-                // CommitId = repoInfo.RemoteHeadCommitId,
-                RemoteBranch = repoInfo.RemoteBranch,
-                RemoteRepositoryUrl = repoInfo.RemoteOriginUrl,
-                RelativePath = PathUtility.MakeRelativePath(repoInfo.RepoRootPath, filePath)
-            };
-        }
-
-        private static GitRepoInfo GetRepoInfoCore(string directory)
-        {
-            var repoRootPath = RunGitCommandAndGetLastLine(directory, GetRepoRootCommand);
-
-            // the path of repo root got from git config file should be the same with path got from git command
-            Debug.Assert(FilePathComparer.OSPlatformSensitiveComparer.Equals(repoRootPath, directory));
-
-            var branchNames = GetBranchNames(repoRootPath);
-
-            var originUrl = RunGitCommandAndGetLastLine(repoRootPath, GetOriginUrlCommand);
-            var repoInfo = new GitRepoInfo
-            {
-                // TODO: remove commit id to avoid config hash changed
-                //LocalHeadCommitId = RunGitCommandAndGetFirstLine(repoRootPath, GetLocalHeadIdCommand),
-                //RemoteHeadCommitId = TryRunGitCommandAndGetFirstLine(repoRootPath, GetRemoteHeadIdCommand),
-                RemoteOriginUrl = originUrl,
-                RepoRootPath = repoRootPath,
-                LocalBranch = branchNames.Item1,
-                RemoteBranch = branchNames.Item2
-            };
-
-            return repoInfo;
-        }
-
-        private static Tuple<string, string> GetBranchNames(string repoRootPath)
-        {
-            // Use the branch name specified by the environment variable.
-            var localBranch = Environment.GetEnvironmentVariable("DOCFX_SOURCE_BRANCH_NAME");
-            if (!string.IsNullOrEmpty(localBranch))
-            {
-                Logger.LogInfo($"For git repo <{repoRootPath}>, using branch '{localBranch}' from the environment variable DOCFX_SOURCE_BRANCH_NAME.");
-                return Tuple.Create(localBranch, localBranch);
-            }
-
-            var isDetachedHead = "HEAD" == RunGitCommandAndGetLastLine(repoRootPath, GetLocalBranchCommand);
-            if (isDetachedHead)
-            {
-                return GetBranchNamesFromDetachedHead(repoRootPath);
-            }
-
-            localBranch = RunGitCommandAndGetLastLine(repoRootPath, GetLocalBranchCommand);
-            string remoteBranch;
-            try
-            {
-                remoteBranch = RunGitCommandAndGetLastLine(repoRootPath, GetRemoteBranchCommand);
-                var index = remoteBranch.IndexOf('/');
-                if (index > 0)
-                {
-                    remoteBranch = remoteBranch.Substring(index + 1);
-                }
-            }
-            catch (Exception ex)
-            {
-                Logger.LogInfo($"For git repo <{repoRootPath}>, can't find remote branch in this repo and fallback to use local branch [{localBranch}]: {ex.Message}");
-                remoteBranch = localBranch;
-            }
-            return Tuple.Create(localBranch, remoteBranch);
-        }
-
-        // Many build systems use a "detached head", which means that the normal git commands
-        // to get branch names do not work. Thankfully, they set an environment variable.
-        private static Tuple<string, string> GetBranchNamesFromDetachedHead(string repoRootPath)
-        {
-            foreach (var name in BuildSystemBranchName)
-            {
-                var branchName = Environment.GetEnvironmentVariable(name);
-                if (!string.IsNullOrEmpty(branchName))
-                {
-                    Logger.LogInfo($"For git repo <{repoRootPath}>, using branch '{branchName}' from the environment variable {name}.");
-                    return Tuple.Create(branchName, branchName);
-                }
-            }
-
-            // Use the comment id as the branch name.
-            var commitId = RunGitCommandAndGetLastLine(repoRootPath, GetLocalBranchCommitIdCommand);
-            Logger.LogInfo($"For git repo <{repoRootPath}>, using commit id {commitId} as the branch name.");
-            return Tuple.Create(commitId, commitId);
-        }
-
-        private static void ProcessErrorMessage(string message)
-        {
-            throw new GitException(message);
-        }
-
+            }
+
+            var path = Path.Combine(EnvironmentContext.BaseDirectory, filePath).ToNormalizedPath();
+
+            var detail = GetFileDetailCore(path);
+            return detail;
+        }
+
+        public static GitRepoInfo GetRepoInfo(string directory)
+        {
+            if (directory == null)
+            {
+                return null;
+            }
+
+            if (IsGitRoot(directory))
+            {
+                return Cache.GetOrAdd(directory, GetRepoInfoCore);
+            }
+
+            var parentDirInfo = Directory.GetParent(directory);
+            if (parentDirInfo == null)
+            {
+                return null;
+            }
+
+            return Cache.GetOrAdd(directory, d => GetRepoInfo(parentDirInfo.FullName));
+        }
+
+        #region Private Methods
+
+        private static bool IsGitRoot(string directory)
+        {
+            var gitPath = Path.Combine(directory, ".git");
+
+            // git submodule contains only a .git file instead of a .git folder
+            return Directory.Exists(gitPath) || File.Exists(gitPath);
+        }
+
+        private static GitDetail GetFileDetailCore(string filePath)
+        {
+            string directory;
+            if (PathUtility.IsDirectory(filePath))
+            {
+                directory = filePath;
+            }
+            else
+            {
+                directory = Path.GetDirectoryName(filePath);
+            }
+
+            var repoInfo = Cache.GetOrAdd(directory, GetRepoInfo);
+
+            return new GitDetail
+            {
+                // TODO: remove commit id to avoid config hash changed
+                // CommitId = repoInfo.RemoteHeadCommitId,
+                RemoteBranch = repoInfo.RemoteBranch,
+                RemoteRepositoryUrl = repoInfo.RemoteOriginUrl,
+                RelativePath = PathUtility.MakeRelativePath(repoInfo.RepoRootPath, filePath)
+            };
+        }
+
+        private static GitRepoInfo GetRepoInfoCore(string directory)
+        {
+            var repoRootPath = RunGitCommandAndGetLastLine(directory, GetRepoRootCommand);
+
+            // the path of repo root got from git config file should be the same with path got from git command
+            Debug.Assert(FilePathComparer.OSPlatformSensitiveComparer.Equals(repoRootPath, directory));
+
+            var branchNames = GetBranchNames(repoRootPath);
+
+            var originUrl = RunGitCommandAndGetLastLine(repoRootPath, GetOriginUrlCommand);
+            var repoInfo = new GitRepoInfo
+            {
+                // TODO: remove commit id to avoid config hash changed
+                //LocalHeadCommitId = RunGitCommandAndGetFirstLine(repoRootPath, GetLocalHeadIdCommand),
+                //RemoteHeadCommitId = TryRunGitCommandAndGetFirstLine(repoRootPath, GetRemoteHeadIdCommand),
+                RemoteOriginUrl = originUrl,
+                RepoRootPath = repoRootPath,
+                LocalBranch = branchNames.Item1,
+                RemoteBranch = branchNames.Item2
+            };
+
+            return repoInfo;
+        }
+
+        private static Tuple<string, string> GetBranchNames(string repoRootPath)
+        {
+            // Use the branch name specified by the environment variable.
+            var localBranch = Environment.GetEnvironmentVariable("DOCFX_SOURCE_BRANCH_NAME");
+            if (!string.IsNullOrEmpty(localBranch))
+            {
+                Logger.LogInfo($"For git repo <{repoRootPath}>, using branch '{localBranch}' from the environment variable DOCFX_SOURCE_BRANCH_NAME.");
+                return Tuple.Create(localBranch, localBranch);
+            }
+
+            var isDetachedHead = "HEAD" == RunGitCommandAndGetLastLine(repoRootPath, GetLocalBranchCommand);
+            if (isDetachedHead)
+            {
+                return GetBranchNamesFromDetachedHead(repoRootPath);
+            }
+
+            localBranch = RunGitCommandAndGetLastLine(repoRootPath, GetLocalBranchCommand);
+            string remoteBranch;
+            try
+            {
+                remoteBranch = RunGitCommandAndGetLastLine(repoRootPath, GetRemoteBranchCommand);
+                var index = remoteBranch.IndexOf('/');
+                if (index > 0)
+                {
+                    remoteBranch = remoteBranch.Substring(index + 1);
+                }
+            }
+            catch (Exception ex)
+            {
+                Logger.LogInfo($"For git repo <{repoRootPath}>, can't find remote branch in this repo and fallback to use local branch [{localBranch}]: {ex.Message}");
+                remoteBranch = localBranch;
+            }
+            return Tuple.Create(localBranch, remoteBranch);
+        }
+
+        // Many build systems use a "detached head", which means that the normal git commands
+        // to get branch names do not work. Thankfully, they set an environment variable.
+        private static Tuple<string, string> GetBranchNamesFromDetachedHead(string repoRootPath)
+        {
+            foreach (var name in BuildSystemBranchName)
+            {
+                var branchName = Environment.GetEnvironmentVariable(name);
+                if (!string.IsNullOrEmpty(branchName))
+                {
+                    Logger.LogInfo($"For git repo <{repoRootPath}>, using branch '{branchName}' from the environment variable {name}.");
+                    return Tuple.Create(branchName, branchName);
+                }
+            }
+
+            // Use the comment id as the branch name.
+            var commitId = RunGitCommandAndGetLastLine(repoRootPath, GetLocalBranchCommitIdCommand);
+            Logger.LogInfo($"For git repo <{repoRootPath}>, using commit id {commitId} as the branch name.");
+            return Tuple.Create(commitId, commitId);
+        }
+
+        private static void ProcessErrorMessage(string message)
+        {
+            throw new GitException(message);
+        }
+
         private static string TryRunGitCommand(string repoPath, string arguments)
         {
-            var content = new StringBuilder();
-            try
-            {
-                RunGitCommand(repoPath, arguments, output => content.AppendLine(output));
-            }
-            catch (Exception)
-            {
-                // ignored
-            }
+            var content = new StringBuilder();
+            try
+            {
+                RunGitCommand(repoPath, arguments, output => content.AppendLine(output));
+            }
+            catch (Exception)
+            {
+                // ignored
+            }
             return content.Length == 0 ? null : content.ToString();
-        }
-
-        private static string TryRunGitCommandAndGetLastLine(string repoPath, string arguments)
-        {
-            string content = null;
-            try
-            {
-                content = RunGitCommandAndGetLastLine(repoPath, arguments);
-            }
-            catch (Exception)
-            {
-                // ignored
-            }
-            return content;
-        }
-
-        private static string RunGitCommandAndGetLastLine(string repoPath, string arguments)
-        {
-            string content = null;
-            RunGitCommand(repoPath, arguments, output => content = output);
-
-            if (string.IsNullOrEmpty(content))
-            {
-                throw new GitException("The result can't be null or empty string");
-            }
-            return content;
-        }
-
-        private static void RunGitCommand(string repoPath, string arguments, Action<string> processOutput)
-        {
-            var encoding = Encoding.UTF8;
-            const int bufferSize = 4096;
-
-            if (!Directory.Exists(repoPath))
-            {
-                throw new ArgumentException($"Can't find repo: {repoPath}");
-            }
-
-            using (var outputStream = new MemoryStream())
-            using (var errorStream = new MemoryStream())
-            {
-                int exitCode;
-
-                using (var outputStreamWriter = new StreamWriter(outputStream, encoding, bufferSize, true))
-                using (var errorStreamWriter = new StreamWriter(errorStream, encoding, bufferSize, true))
-                {
-                    exitCode = CommandUtility.RunCommand(new CommandInfo
-                    {
-                        Name = CommandName,
-                        Arguments = arguments,
-                        WorkingDirectory = repoPath,
-                    }, outputStreamWriter, errorStreamWriter, GitTimeOut);
-                }
-
-                if (exitCode != 0)
-                {
-                    errorStream.Position = 0;
-                    using (var errorStreamReader = new StreamReader(errorStream, encoding, false, bufferSize, true))
-                    {
-                        ProcessErrorMessage(errorStreamReader.ReadToEnd());
-                    }
-                }
-                else
-                {
-                    outputStream.Position = 0;
-                    using (var streamReader = new StreamReader(outputStream, encoding, false, bufferSize, true))
-                    {
-                        string line;
-                        while ((line = streamReader.ReadLine()) != null)
-                        {
-                            processOutput(line);
-                        }
-                    }
-                }
-            }
-        }
-
-        private static bool ExistGitCommand()
-        {
+        }
+
+        private static string TryRunGitCommandAndGetLastLine(string repoPath, string arguments)
+        {
+            string content = null;
+            try
+            {
+                content = RunGitCommandAndGetLastLine(repoPath, arguments);
+            }
+            catch (Exception)
+            {
+                // ignored
+            }
+            return content;
+        }
+
+        private static string RunGitCommandAndGetLastLine(string repoPath, string arguments)
+        {
+            string content = null;
+            RunGitCommand(repoPath, arguments, output => content = output);
+
+            if (string.IsNullOrEmpty(content))
+            {
+                throw new GitException("The result can't be null or empty string");
+            }
+            return content;
+        }
+
+        private static void RunGitCommand(string repoPath, string arguments, Action<string> processOutput)
+        {
+            var encoding = Encoding.UTF8;
+            const int bufferSize = 4096;
+
+            if (!Directory.Exists(repoPath))
+            {
+                throw new ArgumentException($"Can't find repo: {repoPath}");
+            }
+
+            using (var outputStream = new MemoryStream())
+            using (var errorStream = new MemoryStream())
+            {
+                int exitCode;
+
+                using (var outputStreamWriter = new StreamWriter(outputStream, encoding, bufferSize, true))
+                using (var errorStreamWriter = new StreamWriter(errorStream, encoding, bufferSize, true))
+                {
+                    exitCode = CommandUtility.RunCommand(new CommandInfo
+                    {
+                        Name = CommandName,
+                        Arguments = arguments,
+                        WorkingDirectory = repoPath,
+                    }, outputStreamWriter, errorStreamWriter, GitTimeOut);
+                }
+
+                if (exitCode != 0)
+                {
+                    errorStream.Position = 0;
+                    using (var errorStreamReader = new StreamReader(errorStream, encoding, false, bufferSize, true))
+                    {
+                        ProcessErrorMessage(errorStreamReader.ReadToEnd());
+                    }
+                }
+                else
+                {
+                    outputStream.Position = 0;
+                    using (var streamReader = new StreamReader(outputStream, encoding, false, bufferSize, true))
+                    {
+                        string line;
+                        while ((line = streamReader.ReadLine()) != null)
+                        {
+                            processOutput(line);
+                        }
+                    }
+                }
+            }
+        }
+
+        private static bool ExistGitCommand()
+        {
             if (GitCommandExists == null)
             {
                 lock (SyncRoot)
@@ -305,12 +305,17 @@
                     if (GitCommandExists == null)
                     {
                         GitCommandExists = CommandUtility.ExistCommand(CommandName);
+                        if (GitCommandExists != true)
+                        {
+                            Logger.LogInfo("Looks like Git is not installed globally. We depend on Git to extract repository information for source code and files.");
+                        }
                     }
                 }
-            }
-            return GitCommandExists.Value;
-        }
-
-        #endregion
-    }
-}
+            }
+
+            return GitCommandExists.Value;
+        }
+
+        #endregion
+    }
+}


Fixed a bug that prevented the settings page from loading.

--- src/Neptunium/ViewModel/SettingsViewViewModel.cs
+++ src/Neptunium/ViewModel/SettingsViewViewModel.cs
@@ -50,7 +50,7 @@
         {
             CarModeAnnounceSongs = CarModeManager.ShouldAnnounceSongs;

-            SelectedBluetoothDevice = CarModeManager.SelectedDevice.Name;
+            SelectedBluetoothDevice = CarModeManager.SelectedDevice?.Name;

             ShouldShowSongNofitications = (bool)ApplicationData.Current.LocalSettings.Values[AppSettings.ShowSongNotifications];
         }


GetValueOrDefault is now in CollectionExtensions

--- src/NodaTime.Web/Helpers/DictionaryExtensions.cs
+++ src/NodaTime.Web/Helpers/DictionaryExtensions.cs
@@ -1,16 +0,0 @@
-// Copyright 2017 The Noda Time Authors. All rights reserved.
-// Use of this source code is governed by the Apache License 2.0,
-// as found in the LICENSE.txt file.
-using System.Collections.Generic;
-
-namespace NodaTime.Web.Helpers
-{
-    public static class DictionaryExtensions
-    {
-        public static TValue GetValueOrDefault<TKey, TValue>(this IDictionary<TKey, TValue> dictionary, TKey key)
-        {
-            dictionary.TryGetValue(key, out var value);
-            return value;
-        }
-    }
-}


Make it just 'sign in' instead of 'sign in / register'

--- src/NuGetGallery/Views/Shared/UserDisplay.cshtml
+++ src/NuGetGallery/Views/Shared/UserDisplay.cshtml
@@ -3,7 +3,7 @@
     {
         string returnUrl = ViewData.ContainsKey(Constants.ReturnUrlViewDataKey) ? (string)ViewData[Constants.ReturnUrlViewDataKey] : Request.RawUrl;

-            Sign in / Register
+            Sign in

     }
     else


Add bool AreFinesEnabled to Setting Entity.

--- src/Open-School-Library/Data/Entities/Setting.cs
+++ src/Open-School-Library/Data/Entities/Setting.cs
@@ -9,6 +9,7 @@
     public class Setting
     {
         public int SettingID { get; set; }
+        public bool AreFinesEnabled { get; set; }
         [Column(TypeName = "Money")]
         public decimal? FineAmountPerDay { get; set; }
         public int CheckoutDurationInDays { get; set; }


Rework the encryption/signing development certificates mechanism to support multiple certificates

--- src/OpenIddict.Server/OpenIddictServerBuilder.cs
+++ src/OpenIddict.Server/OpenIddictServerBuilder.cs
@@ -217,29 +217,21 @@
             using var store = new X509Store(StoreName.My, StoreLocation.CurrentUser);
             store.Open(OpenFlags.ReadWrite);

-            // Try to retrieve the development certificate from the specified store.
-            // If a certificate was found but is not yet or no longer valid, remove it
-            // from the store before creating and persisting a new encryption certificate.
-            var certificate = store.Certificates.Find(X509FindType.FindBySubjectDistinguishedName, subject.Name, validOnly: false)
+            // Try to retrieve the existing development certificates from the specified store.
+            // If no valid existing certificate was found, create a new encryption certificate.
+            var certificates = store.Certificates.Find(X509FindType.FindBySubjectDistinguishedName, subject.Name, validOnly: false)
                 .OfType<X509Certificate2>()
-                .SingleOrDefault();
-
-            if (certificate is not null && (certificate.NotBefore > DateTime.Now || certificate.NotAfter < DateTime.Now))
-            {
-                store.Remove(certificate);
-                certificate = null;
-            }
-
+                .ToList();
+
+            if (!certificates.Any(certificate => certificate.NotBefore < DateTime.Now && certificate.NotAfter > DateTime.Now))
+            {
 #if SUPPORTS_CERTIFICATE_GENERATION
-            // If no appropriate certificate can be found, generate and persist a new certificate in the specified store.
-            if (certificate is null)
-            {
                 using var algorithm = RSA.Create(keySizeInBits: 2048);

                 var request = new CertificateRequest(subject, algorithm, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
                 request.CertificateExtensions.Add(new X509KeyUsageExtension(X509KeyUsageFlags.KeyEncipherment, critical: true));

-                certificate = request.CreateSelfSigned(DateTimeOffset.UtcNow, DateTimeOffset.UtcNow.AddYears(2));
+                var certificate = request.CreateSelfSigned(DateTimeOffset.UtcNow, DateTimeOffset.UtcNow.AddYears(2));

                 // Note: setting the friendly name is not supported on Unix machines (including Linux and macOS).
                 // To ensure an exception is not thrown by the property setter, an OS runtime check is used here.
@@ -265,7 +257,7 @@
                         flags |= X509KeyStorageFlags.Exportable;
                     }

-                    certificate = new X509Certificate2(data, string.Empty, flags);
+                    certificates.Insert(0, certificate = new X509Certificate2(data, string.Empty, flags));
                 }

                 finally
@@ -274,12 +266,15 @@
                 }

                 store.Add(certificate);
-            }
-
-            return AddEncryptionCertificate(certificate);
 #else
-            throw new PlatformNotSupportedException(SR.GetResourceString(SR.ID0264));
+                throw new PlatformNotSupportedException(SR.GetResourceString(SR.ID0264));
 #endif
+            }
+
+            return Configure(options => options.EncryptionCredentials.AddRange(
+                from certificate in certificates
+                let key = new X509SecurityKey(certificate)
+                select new EncryptingCredentials(key, SecurityAlgorithms.RsaOAEP, SecurityAlgorithms.Aes256CbcHmacSha512)));
         }

         /// <summary>
@@ -654,29 +649,21 @@
             using var store = new X509Store(StoreName.My, StoreLocation.CurrentUser);
             store.Open(OpenFlags.ReadWrite);

-            // Try to retrieve the development certificate from the specified store.
-            // If a certificate was found but is not yet or no longer valid, remove it
-            // from the store before creating and persisting a new signing certificate.
-            var certificate = store.Certificates.Find(X509FindType.FindBySubjectDistinguishedName, subject.Name, validOnly: false)
+            // Try to retrieve the existing development certificates from the specified store.
+            // If no valid existing certificate was found, create a new signing certificate.
+            var certificates = store.Certificates.Find(X509FindType.FindBySubjectDistinguishedName, subject.Name, validOnly: false)
                 .OfType<X509Certificate2>()
-                .SingleOrDefault();
-
-            if (certificate is not null && (certificate.NotBefore > DateTime.Now || certificate.NotAfter < DateTime.Now))
-            {
-                store.Remove(certificate);
-                certificate = null;
-            }
-
+                .ToList();
+
+            if (!certificates.Any(certificate => certificate.NotBefore < DateTime.Now && certificate.NotAfter > DateTime.Now))
+            {
 #if SUPPORTS_CERTIFICATE_GENERATION
-            // If no appropriate certificate can be found, generate and persist a new certificate in the specified store.
-            if (certificate is null)
-            {
                 using var algorithm = RSA.Create(keySizeInBits: 2048);

                 var request = new CertificateRequest(subject, algorithm, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
                 request.CertificateExtensions.Add(new X509KeyUsageExtension(X509KeyUsageFlags.DigitalSignature, critical: true));

-                certificate = request.CreateSelfSigned(DateTimeOffset.UtcNow, DateTimeOffset.UtcNow.AddYears(2));
+                var certificate = request.CreateSelfSigned(DateTimeOffset.UtcNow, DateTimeOffset.UtcNow.AddYears(2));

                 // Note: setting the friendly name is not supported on Unix machines (including Linux and macOS).
                 // To ensure an exception is not thrown by the property setter, an OS runtime check is used here.
@@ -702,7 +689,7 @@
                         flags |= X509KeyStorageFlags.Exportable;
                     }

-                    certificate = new X509Certificate2(data, string.Empty, flags);
+                    certificates.Insert(0, certificate = new X509Certificate2(data, string.Empty, flags));
                 }

                 finally
@@ -711,12 +698,15 @@
                 }

                 store.Add(certificate);
-            }
-
-            return AddSigningCertificate(certificate);
 #else
-            throw new PlatformNotSupportedException(SR.GetResourceString(SR.ID0264));
+                throw new PlatformNotSupportedException(SR.GetResourceString(SR.ID0264));
 #endif
+            }
+
+            return Configure(options => options.SigningCredentials.AddRange(
+                from certificate in certificates
+                let key = new X509SecurityKey(certificate)
+                select new SigningCredentials(key, SecurityAlgorithms.RsaSha256)));
         }

         /// <summary>


Fix NullReferenceException in ExecutingWorkItemsTracker (#4850)

--- src/Orleans.Core/Threading/ThreadPoolExecutor.cs
+++ src/Orleans.Core/Threading/ThreadPoolExecutor.cs
@@ -39,15 +39,11 @@
             this.schedulerStatistics = schedulerStatistics;
             this.schedulerStageStatistics = schedulerStageStatistics;
             this.statisticsOptions = statisticsOptions;
-
-            workQueue = new ThreadPoolWorkQueue();
-
-            statistic = new ThreadPoolTrackingStatistic(options.Name, options.LoggerFactory, statisticsOptions, schedulerStageStatistics);
-
-            executingWorkTracker = new ExecutingWorkItemsTracker(this);
-
-            log = options.LoggerFactory.CreateLogger<ThreadPoolExecutor>();
-
+            this.workQueue = new ThreadPoolWorkQueue();
+            this.statistic = new ThreadPoolTrackingStatistic(options.Name, options.LoggerFactory, statisticsOptions, schedulerStageStatistics);
+            this.log = options.LoggerFactory.CreateLogger<ThreadPoolExecutor>();
+            this.executingWorkTracker = new ExecutingWorkItemsTracker(options, this.log);
+
             options.CancellationTokenSource.Token.Register(Complete);

             for (var threadIndex = 0; threadIndex < options.DegreeOfParallelism; threadIndex++)
@@ -214,10 +210,11 @@

             private readonly ILogger log;

-            public ExecutingWorkItemsTracker(ThreadPoolExecutor executor)
-            {
-                runningItems = new WorkItem[GetThreadSlot(executor.options.DegreeOfParallelism)];
-                log = executor.log;
+            public ExecutingWorkItemsTracker(ThreadPoolExecutorOptions options, ILogger log)
+            {
+                if (options == null) throw new ArgumentNullException(nameof(options));
+                this.runningItems = new WorkItem[GetThreadSlot(options.DegreeOfParallelism)];
+                this.log = log ?? throw new ArgumentNullException(nameof(log));
             }

             public override void OnActionExecuting(ExecutionContext context)
@@ -238,7 +235,7 @@
                     if (workItem != null && workItem.IsFrozen())
                     {
                         frozen = true;
-                        log.Error(
+                        this.log.Error(
                             ErrorCode.ExecutorTurnTooLong,
                             string.Format(SR.WorkItem_LongExecutionTime, workItem.GetWorkItemStatus(true)));
                     }


fixed: PhpSqlDbConnection doesn't set the ConnectionString property.

--- src/Peachpie.Library.MsSql/PhpSqlDbConnection.cs
+++ src/Peachpie.Library.MsSql/PhpSqlDbConnection.cs
@@ -72,7 +72,7 @@

             //_ctx = ctx;
             _manager = manager;
-            _connection = new SqlConnection();
+            _connection = new SqlConnection(connectionString);
             // TODO: Connection.InfoMessage += new SqlInfoMessageEventHandler(InfoMessage);
         }


Fix ambiguous attribute error when inspecting PhpStackTrace

--- src/Peachpie.Runtime/Reflection/PhpStackTrace.cs
+++ src/Peachpie.Runtime/Reflection/PhpStackTrace.cs
@@ -285,7 +285,7 @@

                     if (tinfo.IsPublic && tinfo.IsAbstract) // => public static
                     {
-                        if (tinfo.Assembly.GetCustomAttribute<PhpExtensionAttribute>() != null)
+                        if (tinfo.Assembly.IsDefined(typeof(PhpExtensionAttribute)))
                         {
                             // library function
                             return null;


Fixed UnitTest :worried:

Fixed UnitTest :worried:

--- src/SchJan.Akka.Tests/PubSub/UnsubscribeTests.cs
+++ src/SchJan.Akka.Tests/PubSub/UnsubscribeTests.cs
@@ -1,8 +1,3 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
 using Akka.Actor;
 using Akka.TestKit.NUnit;
 using NUnit.Framework;
@@ -23,15 +18,12 @@
                 new SubscribeMessage(terminatedActor, typeof (ActorUnsubscribedMessage)));

             var receiverActor = CreateTestProbe();
-            terminatedActor.Send(pubSubActor,
+            receiverActor.Send(pubSubActor,
                 new SubscribeMessage(receiverActor, typeof (ActorUnsubscribedMessage)));

             terminatedActor.Tell(PoisonPill.Instance);

-            var message = receiverActor.ExpectMsg<ActorUnsubscribedMessage>();
-
-            Assert.That(message.Terminated, Is.True);
-            Assert.That(message.Actor, Is.EqualTo(terminatedActor));
+            receiverActor.ExpectMsg<ActorUnsubscribedMessage>(msg => msg.Terminated && msg.Actor.Equals(terminatedActor));
         }

         [Test]
@@ -44,15 +36,13 @@
                 new SubscribeMessage(terminatedActor, typeof (ActorUnsubscribedMessage)));

             var receiverActor = CreateTestProbe();
-            terminatedActor.Send(pubSubActor,
+            receiverActor.Send(pubSubActor,
                 new SubscribeMessage(receiverActor, typeof (ActorUnsubscribedMessage)));

             terminatedActor.Send(pubSubActor, new UnsubscribeMessage(terminatedActor));

-            var message = receiverActor.ExpectMsg<ActorUnsubscribedMessage>();
-
-            Assert.That(message.Terminated, Is.False);
-            Assert.That(message.Actor, Is.EqualTo(terminatedActor));
+            receiverActor.ExpectMsg<ActorUnsubscribedMessage>(
+                msg => !msg.Terminated && msg.Actor.Equals(terminatedActor));
         }

         [PublishMessage(typeof (ActorUnsubscribedMessage))]


Use simplified methods for press/release key

--- osu.Game.Rulesets.Osu.Tests/Editor/TestSceneObjectObjectSnap.cs
+++ osu.Game.Rulesets.Osu.Tests/Editor/TestSceneObjectObjectSnap.cs
@@ -34,19 +34,9 @@
             AddStep("move mouse to centre", () => InputManager.MoveMouseTo(playfield.ScreenSpaceDrawQuad.Centre));

             if (!distanceSnapEnabled)
-            {
-                AddStep("disable distance snap", () =>
-                {
-                    InputManager.PressKey(Key.Q);
-                    InputManager.ReleaseKey(Key.Q);
-                });
-            }
+                AddStep("disable distance snap", () => InputManager.Key(Key.Q));

-            AddStep("enter placement mode", () =>
-            {
-                InputManager.PressKey(Key.Number2);
-                InputManager.ReleaseKey(Key.Number2);
-            });
+            AddStep("enter placement mode", () => InputManager.Key(Key.Number2));

             AddStep("place first object", () => InputManager.Click(MouseButton.Left));

@@ -70,17 +60,9 @@
         {
             AddStep("move mouse to centre", () => InputManager.MoveMouseTo(playfield.ScreenSpaceDrawQuad.Centre));

-            AddStep("disable distance snap", () =>
-            {
-                InputManager.PressKey(Key.Q);
-                InputManager.ReleaseKey(Key.Q);
-            });
+            AddStep("disable distance snap", () => InputManager.Key(Key.Q));

-            AddStep("enter slider placement mode", () =>
-            {
-                InputManager.PressKey(Key.Number3);
-                InputManager.ReleaseKey(Key.Number3);
-            });
+            AddStep("enter slider placement mode", () => InputManager.Key(Key.Number3));

             AddStep("start slider placement", () => InputManager.Click(MouseButton.Left));

@@ -88,11 +70,7 @@

             AddStep("end slider placement", () => InputManager.Click(MouseButton.Right));

-            AddStep("enter circle placement mode", () =>
-            {
-                InputManager.PressKey(Key.Number2);
-                InputManager.ReleaseKey(Key.Number2);
-            });
+            AddStep("enter circle placement mode", () => InputManager.Key(Key.Number2));

             AddStep("move mouse slightly", () => InputManager.MoveMouseTo(playfield.ScreenSpaceDrawQuad.Centre + new Vector2(playfield.ScreenSpaceDrawQuad.Width * 0.20f, 0)));


Add failing replay button enabled test asserts

--- osu.Game.Tests/Visual/Gameplay/TestSceneReplayDownloadButton.cs
+++ osu.Game.Tests/Visual/Gameplay/TestSceneReplayDownloadButton.cs
@@ -150,10 +150,12 @@
             AddStep("import score", () => imported = scoreManager.Import(getScoreInfo(true)));

             AddUntilStep("state is available", () => downloadButton.State.Value == DownloadState.LocallyAvailable);
+            AddAssert("button is enabled", () => downloadButton.ChildrenOfType<DownloadButton>().First().Enabled.Value);

             AddStep("delete score", () => scoreManager.Delete(imported.Value));

             AddUntilStep("state is not downloaded", () => downloadButton.State.Value == DownloadState.NotDownloaded);
+            AddAssert("button is not enabled", () => !downloadButton.ChildrenOfType<DownloadButton>().First().Enabled.Value);
         }

         [Test]


Fix some more wrong icon colours

Fix some more wrong icon colours

--- osu.Game/Overlays/Profile/Sections/Recent/RecentActivityIcon.cs
+++ osu.Game/Overlays/Profile/Sections/Recent/RecentActivityIcon.cs
@@ -62,12 +62,12 @@

                 case RecentActivityType.BeatmapsetUpdate:
                     icon.Icon = FontAwesome.Solid.SyncAlt;
-                    icon.Colour = colours.Lime1;
+                    icon.Colour = colours.Green1;
                     break;

                 case RecentActivityType.BeatmapsetUpload:
                     icon.Icon = FontAwesome.Solid.ArrowUp;
-                    icon.Colour = colours.Yellow;
+                    icon.Colour = colours.Orange1;
                     break;

                 case RecentActivityType.RankLost:


Change timer interval to 1 minute.

Change timer interval to 1 minute.

--- JabbR/App_Start/Startup.BackgroundWork.cs
+++ JabbR/App_Start/Startup.BackgroundWork.cs
@@ -16,7 +16,7 @@
         // Background task info
         private static volatile bool _sweeping;
         private static Timer _backgroundTimer;
-        private static readonly TimeSpan _sweepInterval = TimeSpan.FromMinutes(2);
+        private static readonly TimeSpan _sweepInterval = TimeSpan.FromMinutes(1);
         private static readonly TimeSpan _sweepStart = TimeSpan.FromMinutes(10);

         private static void StartBackgroundWork(IKernel kernel, IDependencyResolver resolver)


Fixed typo in exception message

Fixed typo in exception message

--- JsonFlatFileDataStore/ObjectExtensions.cs
+++ JsonFlatFileDataStore/ObjectExtensions.cs
@@ -14,7 +14,7 @@
     public static void CopyProperties(this object source, object destination)
     {
         if (source == null || destination == null)
-            throw new Exception("Source or/and Destination óbjects are null");
+            throw new Exception("source or/and destination objects are null");

         if (destination.GetType() == typeof(ExpandoObject))
             HandleExpando(source, destination);


New validation

New validation

--- AngleSharp/DOM/Css/Properties/BorderRadius/CSSBorderRadiusPartProperty.cs
+++ AngleSharp/DOM/Css/Properties/BorderRadius/CSSBorderRadiusPartProperty.cs
@@ -11,8 +11,8 @@
     {
         #region Fields

-        IDistance _h;
-        IDistance _v;
+        IDistance _horizontal;
+        IDistance _vertical;

         #endregion

@@ -33,7 +33,7 @@
         /// </summary>
         public IDistance HorizontalRadius
         {
-            get { return _h; }
+            get { return _horizontal; }
         }

         /// <summary>
@@ -41,7 +41,7 @@
         /// </summary>
         public Boolean IsCircle
         {
-            get { return _h.Equals(_v); }
+            get { return _horizontal.Equals(_vertical); }
         }

         /// <summary>
@@ -49,17 +49,23 @@
         /// </summary>
         public IDistance VerticalRadius
         {
-            get { return _v; }
+            get { return _vertical; }
         }

         #endregion

         #region Methods

+        public void SetRadius(IDistance horizontal, IDistance vertical)
+        {
+            _horizontal = horizontal;
+            _vertical = vertical;
+        }
+
         internal override void Reset()
         {
-            _h = Percent.Zero;
-            _v = Percent.Zero;
+            _horizontal = Percent.Zero;
+            _vertical = Percent.Zero;
         }

         /// <summary>
@@ -69,28 +75,7 @@
         /// <returns>True if the state is valid, otherwise false.</returns>
         protected override Boolean IsValid(CSSValue value)
         {
-            var list = value as CSSValueList;
-            var v1 = value;
-            var v2 = value;
-
-            if (list != null)
-            {
-                if (list.Length != 2)
-                    return false;
-
-                v1 = list[0];
-                v2 = list[1];
-            }
-
-            var c1 = v1.ToDistance();
-            var c2 = v2.ToDistance();
-
-            if (c1 == null || c2 == null)
-                return false;
-
-            _h = c1;
-            _v = c2;
-            return true;
+            return this.TakeMany(this.WithDistance()).Constraint(m => m.Length < 3).TryConvert(value, m => SetRadius(m[0], m.Length == 2 ? m[1] : m[0]));
         }

         #endregion


Fix catch hyper dash colour defaults not being set correctly

As the defaults were not set, if a skin happened to specify 0,0,0,0 it
would be ignored due to the early returns in property setters.

--- osu.Game.Rulesets.Catch/UI/CatcherTrailDisplay.cs
+++ osu.Game.Rulesets.Catch/UI/CatcherTrailDisplay.cs
@@ -24,7 +24,7 @@
         private readonly Container<CatcherTrailSprite> hyperDashTrails;
         private readonly Container<CatcherTrailSprite> endGlowSprites;

-        private Color4 hyperDashTrailsColour;
+        private Color4 hyperDashTrailsColour = Catcher.DEFAULT_HYPER_DASH_COLOUR;

         public Color4 HyperDashTrailsColour
         {
@@ -39,7 +39,7 @@
             }
         }

-        private Color4 endGlowSpritesColour;
+        private Color4 endGlowSpritesColour = Catcher.DEFAULT_HYPER_DASH_COLOUR;

         public Color4 EndGlowSpritesColour
         {