diff --git a/src/UniGetUI.Core.LanguageEngine/Assets/Data/LanguagesReference.json b/src/UniGetUI.Core.LanguageEngine/Assets/Data/LanguagesReference.json index 940c63b497..8a5c5b44fb 100644 --- a/src/UniGetUI.Core.LanguageEngine/Assets/Data/LanguagesReference.json +++ b/src/UniGetUI.Core.LanguageEngine/Assets/Data/LanguagesReference.json @@ -12,10 +12,12 @@ "et": "Estonian - Eesti", "en": "English - English", "es": "Spanish - Castellano", + "es-MX": "Spanish (Mexico)", "fa": "Persian - فارسی‎", "fi": "Finnish - Suomi", "fil": "Filipino - Filipino", "fr": "French - Français", + "gl": "Galician - Galego", "gu": "Gujarati - ગુજરાતી", "hi": "Hindi - हिंदी", "hr": "Croatian - Hrvatski", diff --git a/src/UniGetUI.PAckageEngine.Interfaces/IInstallationOptions.cs b/src/UniGetUI.PAckageEngine.Interfaces/IInstallationOptions.cs index 5f16e55490..1718e6ea7e 100644 --- a/src/UniGetUI.PAckageEngine.Interfaces/IInstallationOptions.cs +++ b/src/UniGetUI.PAckageEngine.Interfaces/IInstallationOptions.cs @@ -17,44 +17,23 @@ public interface IInstallationOptions public bool RemoveDataOnUninstall { get; set; } public bool PreRelease { get; set; } public string CustomInstallLocation { get; set; } - public IPackage Package { get; } + public bool OverridesNextLevelOpts { get; set; } /// /// Loads and applies the options from the given SerializableInstallationOptions object to the current object. /// - public void FromSerializable(SerializableInstallationOptions options); + public void GetValuesFromSerializable(SerializableInstallationOptions options); /// /// Returns a SerializableInstallationOptions object containing the options of the current instance. /// - public SerializableInstallationOptions AsSerializable(); - - /// - /// Saves the current options to disk, asynchronously. - /// - public async Task SaveToDiskAsync() - { - await Task.Run(SaveToDisk); - } - - /// - /// Loads the options from disk, asynchronously. - /// - public async Task LoadFromDiskAsync() - { - await Task.Run(LoadFromDisk); - } + public SerializableInstallationOptions ToSerializable(); /// /// Saves the current options to disk. /// public void SaveToDisk(); - /// - /// Loads the options from disk. - /// - protected void LoadFromDisk(); - /// /// Returns a string representation of the current options. /// diff --git a/src/UniGetUI.PackageEngine.Managers.Cargo/Helpers/CargoPkgOperationHelper.cs b/src/UniGetUI.PackageEngine.Managers.Cargo/Helpers/CargoPkgOperationHelper.cs index e4da839cc0..23beae6bf2 100644 --- a/src/UniGetUI.PackageEngine.Managers.Cargo/Helpers/CargoPkgOperationHelper.cs +++ b/src/UniGetUI.PackageEngine.Managers.Cargo/Helpers/CargoPkgOperationHelper.cs @@ -4,7 +4,7 @@ namespace UniGetUI.PackageEngine.Managers.CargoManager; -internal sealed class CargoPkgOperationHelper(Cargo cargo) : PackagePkgOperationHelper(cargo) +internal sealed class CargoPkgOperationHelper(Cargo cargo) : BasePkgOperationHelper(cargo) { protected override IReadOnlyList _getOperationParameters(IPackage package, IInstallationOptions options, OperationType operation) { diff --git a/src/UniGetUI.PackageEngine.Managers.Chocolatey/Helpers/ChocolateyPkgOperationHelper.cs b/src/UniGetUI.PackageEngine.Managers.Chocolatey/Helpers/ChocolateyPkgOperationHelper.cs index 54de0ebabd..897a421f0a 100644 --- a/src/UniGetUI.PackageEngine.Managers.Chocolatey/Helpers/ChocolateyPkgOperationHelper.cs +++ b/src/UniGetUI.PackageEngine.Managers.Chocolatey/Helpers/ChocolateyPkgOperationHelper.cs @@ -4,7 +4,7 @@ using UniGetUI.PackageEngine.Interfaces; namespace UniGetUI.PackageEngine.Managers.ChocolateyManager; -internal sealed class ChocolateyPkgOperationHelper : PackagePkgOperationHelper +internal sealed class ChocolateyPkgOperationHelper : BasePkgOperationHelper { public ChocolateyPkgOperationHelper(Chocolatey manager) : base(manager) { } diff --git a/src/UniGetUI.PackageEngine.Managers.Dotnet/Helpers/DotNetPkgOperationHelper.cs b/src/UniGetUI.PackageEngine.Managers.Dotnet/Helpers/DotNetPkgOperationHelper.cs index 4541eaed9d..b360d7a74f 100644 --- a/src/UniGetUI.PackageEngine.Managers.Dotnet/Helpers/DotNetPkgOperationHelper.cs +++ b/src/UniGetUI.PackageEngine.Managers.Dotnet/Helpers/DotNetPkgOperationHelper.cs @@ -4,7 +4,7 @@ using UniGetUI.PackageEngine.Interfaces; namespace UniGetUI.PackageEngine.Managers.DotNetManager; -internal sealed class DotNetPkgOperationHelper : PackagePkgOperationHelper +internal sealed class DotNetPkgOperationHelper : BasePkgOperationHelper { public DotNetPkgOperationHelper(DotNet manager) : base(manager) { } diff --git a/src/UniGetUI.PackageEngine.Managers.Generic.NuGet/BaseNuGet.cs b/src/UniGetUI.PackageEngine.Managers.Generic.NuGet/BaseNuGet.cs index 5cac161458..5d6d2d437c 100644 --- a/src/UniGetUI.PackageEngine.Managers.Generic.NuGet/BaseNuGet.cs +++ b/src/UniGetUI.PackageEngine.Managers.Generic.NuGet/BaseNuGet.cs @@ -59,9 +59,18 @@ protected sealed override IReadOnlyList FindPackages_UnSafe(string quer sources = [ Properties.DefaultSource ]; } + bool canPrerelease = InstallationOptions.LoadForManager(this).PreRelease; + foreach(IManagerSource source in sources) { - Uri? SearchUrl = new($"{source.Url}/Search()?$filter=IsLatestVersion&$orderby=Id&searchTerm='{HttpUtility.UrlEncode(query)}'&targetFramework=''&includePrerelease=false&$skip=0&$top=50&semVerLevel=2.0.0"); + Uri? SearchUrl = new($"{source.Url}/Search()" + + $"?$filter=IsLatestVersion" + + $"&$orderby=Id&searchTerm='{HttpUtility.UrlEncode(query)}'" + + $"&targetFramework=''" + + $"&includePrerelease={(canPrerelease? "true": "false")}" + + $"&$skip=0" + + $"&$top=50" + + $"&semVerLevel=2.0.0"); // Uri SearchUrl = new($"{source.Url}/Search()?$filter=IsLatestVersion&searchTerm=%27{HttpUtility.UrlEncode(query)}%27&targetFramework=%27%27&includePrerelease=false"); logger.Log($"Begin package search with url={SearchUrl} on manager {Name}"); Dictionary AlreadyProcessedPackages = []; @@ -145,6 +154,7 @@ protected override IReadOnlyList GetAvailableUpdates_UnSafe() if (!sourceMapping.ContainsKey(uri)) sourceMapping[uri] = new(); sourceMapping[uri].Add(package); } + bool canPrerelease = InstallationOptions.LoadForManager(this).PreRelease; foreach (var pair in sourceMapping) { @@ -161,7 +171,8 @@ protected override IReadOnlyList GetAvailableUpdates_UnSafe() var SearchUrl = $"{pair.Key.Url.ToString().Trim('/')}/GetUpdates()" + $"?packageIds=%27{HttpUtility.UrlEncode(packageIds.ToString().Trim('|'))}%27" + $"&versions=%27{HttpUtility.UrlEncode(packageVers.ToString().Trim('|'))}%27" + - $"&includePrerelease=0&includeAllVersions=0"; + $"&includePrerelease={(canPrerelease ? "true" : "false")}" + + $"&includeAllVersions=0"; using HttpClient client = new(CoreTools.GenericHttpClientParameters); client.DefaultRequestHeaders.UserAgent.ParseAdd(CoreData.UserAgentString); diff --git a/src/UniGetUI.PackageEngine.Managers.Npm/Helpers/NpmPkgOperationHelper.cs b/src/UniGetUI.PackageEngine.Managers.Npm/Helpers/NpmPkgOperationHelper.cs index 49f57608a4..ec718e7786 100644 --- a/src/UniGetUI.PackageEngine.Managers.Npm/Helpers/NpmPkgOperationHelper.cs +++ b/src/UniGetUI.PackageEngine.Managers.Npm/Helpers/NpmPkgOperationHelper.cs @@ -3,7 +3,7 @@ using UniGetUI.PackageEngine.Interfaces; namespace UniGetUI.PackageEngine.Managers.NpmManager; -internal sealed class NpmPkgOperationHelper : PackagePkgOperationHelper +internal sealed class NpmPkgOperationHelper : BasePkgOperationHelper { public NpmPkgOperationHelper(Npm manager) : base(manager) { } diff --git a/src/UniGetUI.PackageEngine.Managers.Pip/Helpers/PipPkgOperationHelper.cs b/src/UniGetUI.PackageEngine.Managers.Pip/Helpers/PipPkgOperationHelper.cs index 8f9e8c8c2b..10be39572d 100644 --- a/src/UniGetUI.PackageEngine.Managers.Pip/Helpers/PipPkgOperationHelper.cs +++ b/src/UniGetUI.PackageEngine.Managers.Pip/Helpers/PipPkgOperationHelper.cs @@ -3,7 +3,7 @@ using UniGetUI.PackageEngine.Interfaces; namespace UniGetUI.PackageEngine.Managers.PipManager; -internal sealed class PipPkgOperationHelper : PackagePkgOperationHelper +internal sealed class PipPkgOperationHelper : BasePkgOperationHelper { public PipPkgOperationHelper(Pip manager) : base(manager) { } diff --git a/src/UniGetUI.PackageEngine.Managers.PowerShell/Helpers/PowerShellPkgOperationHelper.cs b/src/UniGetUI.PackageEngine.Managers.PowerShell/Helpers/PowerShellPkgOperationHelper.cs index f23005f14e..d7bfa540e6 100644 --- a/src/UniGetUI.PackageEngine.Managers.PowerShell/Helpers/PowerShellPkgOperationHelper.cs +++ b/src/UniGetUI.PackageEngine.Managers.PowerShell/Helpers/PowerShellPkgOperationHelper.cs @@ -3,7 +3,7 @@ using UniGetUI.PackageEngine.Interfaces; namespace UniGetUI.PackageEngine.Managers.PowerShellManager; -internal sealed class PowerShellPkgOperationHelper : PackagePkgOperationHelper +internal sealed class PowerShellPkgOperationHelper : BasePkgOperationHelper { public PowerShellPkgOperationHelper(PowerShell manager) : base(manager) { } diff --git a/src/UniGetUI.PackageEngine.Managers.PowerShell7/Helpers/PowerShell7PkgOperationHelper.cs b/src/UniGetUI.PackageEngine.Managers.PowerShell7/Helpers/PowerShell7PkgOperationHelper.cs index 6bdcbd6ef4..56bc0a32ea 100644 --- a/src/UniGetUI.PackageEngine.Managers.PowerShell7/Helpers/PowerShell7PkgOperationHelper.cs +++ b/src/UniGetUI.PackageEngine.Managers.PowerShell7/Helpers/PowerShell7PkgOperationHelper.cs @@ -3,7 +3,7 @@ using UniGetUI.PackageEngine.Interfaces; namespace UniGetUI.PackageEngine.Managers.PowerShell7Manager; -internal sealed class PowerShell7PkgOperationHelper : PackagePkgOperationHelper +internal sealed class PowerShell7PkgOperationHelper : BasePkgOperationHelper { public PowerShell7PkgOperationHelper(PowerShell7 manager) : base(manager) { } diff --git a/src/UniGetUI.PackageEngine.Managers.Scoop/Helpers/ScoopPkgOperationHelper.cs b/src/UniGetUI.PackageEngine.Managers.Scoop/Helpers/ScoopPkgOperationHelper.cs index 34529da319..c3c6ae2033 100644 --- a/src/UniGetUI.PackageEngine.Managers.Scoop/Helpers/ScoopPkgOperationHelper.cs +++ b/src/UniGetUI.PackageEngine.Managers.Scoop/Helpers/ScoopPkgOperationHelper.cs @@ -4,7 +4,7 @@ using UniGetUI.PackageEngine.Interfaces; namespace UniGetUI.PackageEngine.Managers.ScoopManager; -internal sealed class ScoopPkgOperationHelper : PackagePkgOperationHelper +internal sealed class ScoopPkgOperationHelper : BasePkgOperationHelper { public ScoopPkgOperationHelper(Scoop manager) : base(manager) { } diff --git a/src/UniGetUI.PackageEngine.Managers.Vcpkg/Helpers/VcpkgPkgOperationHelper.cs b/src/UniGetUI.PackageEngine.Managers.Vcpkg/Helpers/VcpkgPkgOperationHelper.cs index 876094dac7..a005acc842 100644 --- a/src/UniGetUI.PackageEngine.Managers.Vcpkg/Helpers/VcpkgPkgOperationHelper.cs +++ b/src/UniGetUI.PackageEngine.Managers.Vcpkg/Helpers/VcpkgPkgOperationHelper.cs @@ -3,7 +3,7 @@ using UniGetUI.PackageEngine.Interfaces; namespace UniGetUI.PackageEngine.Managers.VcpkgManager; -internal sealed class VcpkgPkgOperationHelper : PackagePkgOperationHelper +internal sealed class VcpkgPkgOperationHelper : BasePkgOperationHelper { public VcpkgPkgOperationHelper(Vcpkg manager) : base(manager) { } diff --git a/src/UniGetUI.PackageEngine.Managers.WinGet/Helpers/WinGetPkgOperationHelper.cs b/src/UniGetUI.PackageEngine.Managers.WinGet/Helpers/WinGetPkgOperationHelper.cs index dde8e83c8e..9b9e98a936 100644 --- a/src/UniGetUI.PackageEngine.Managers.WinGet/Helpers/WinGetPkgOperationHelper.cs +++ b/src/UniGetUI.PackageEngine.Managers.WinGet/Helpers/WinGetPkgOperationHelper.cs @@ -9,7 +9,7 @@ using UniGetUI.PackageEngine.Interfaces; namespace UniGetUI.PackageEngine.Managers.WingetManager; -internal sealed class WinGetPkgOperationHelper : PackagePkgOperationHelper +internal sealed class WinGetPkgOperationHelper : BasePkgOperationHelper { public static string GetIdNamePiece(IPackage package) { diff --git a/src/UniGetUI.PackageEngine.PackageLoader/UpgradablePackagesLoader.cs b/src/UniGetUI.PackageEngine.PackageLoader/UpgradablePackagesLoader.cs index 2fa5040f70..e2f6c53634 100644 --- a/src/UniGetUI.PackageEngine.PackageLoader/UpgradablePackagesLoader.cs +++ b/src/UniGetUI.PackageEngine.PackageLoader/UpgradablePackagesLoader.cs @@ -37,7 +37,7 @@ protected override async Task IsPackageValid(IPackage package) IgnoredPackages[package.Id] = package; return false; } - if ((await InstallationOptions.FromPackageAsync(package)).SkipMinorUpdates && package.IsUpdateMinor()) + if ((await InstallationOptions.LoadApplicableAsync(package)).SkipMinorUpdates && package.IsUpdateMinor()) { Logger.Info($"Ignoring package {package.Id} because it is a minor update ({package.VersionString} -> {package.NewVersionString}) and SkipMinorUpdates is set to true."); return false; diff --git a/src/UniGetUI.PackageEngine.PackageManagerClasses/Manager/Helpers/PackagePkgOperationHelper.cs b/src/UniGetUI.PackageEngine.PackageManagerClasses/Manager/Helpers/BasePkgOperationHelper.cs similarity index 69% rename from src/UniGetUI.PackageEngine.PackageManagerClasses/Manager/Helpers/PackagePkgOperationHelper.cs rename to src/UniGetUI.PackageEngine.PackageManagerClasses/Manager/Helpers/BasePkgOperationHelper.cs index 168949683a..2b44f013d3 100644 --- a/src/UniGetUI.PackageEngine.PackageManagerClasses/Manager/Helpers/PackagePkgOperationHelper.cs +++ b/src/UniGetUI.PackageEngine.PackageManagerClasses/Manager/Helpers/BasePkgOperationHelper.cs @@ -5,11 +5,11 @@ namespace UniGetUI.PackageEngine.Classes.Manager.BaseProviders; -public abstract class PackagePkgOperationHelper : IPackageOperationHelper +public abstract class BasePkgOperationHelper : IPackageOperationHelper { protected IPackageManager Manager; - public PackagePkgOperationHelper(IPackageManager manager) + public BasePkgOperationHelper(IPackageManager manager) { Manager = manager; } @@ -30,10 +30,21 @@ public IReadOnlyList GetParameters( IInstallationOptions options, OperationType operation) { - var parameters = _getOperationParameters(package, options, operation); - Logger.Info( - $"Loaded operation parameters for package id={package.Id} on manager {Manager.Name} and operation {operation}: " + - string.Join(' ', parameters)); + var parameters = _getOperationParameters(package, options, operation).ToArray(); + + for (int i = 0; i < parameters.Length; i++) + { + parameters[i] = parameters[i] + .Replace("&", "") + .Replace("|", "") + .Replace(";", "") + .Replace("<", "") + .Replace(">", "") + .Replace("\n", ""); + } + + Logger.Info($"Loaded operation parameters for package id={package.Id} on manager {Manager.Name} and operation {operation}: " + + string.Join(' ', parameters)); return parameters; } diff --git a/src/UniGetUI.PackageEngine.PackageManagerClasses/Packages/Classes/InstallationOptions.cs b/src/UniGetUI.PackageEngine.PackageManagerClasses/Packages/Classes/InstallationOptions.cs index 03881b8822..eefc240669 100644 --- a/src/UniGetUI.PackageEngine.PackageManagerClasses/Packages/Classes/InstallationOptions.cs +++ b/src/UniGetUI.PackageEngine.PackageManagerClasses/Packages/Classes/InstallationOptions.cs @@ -17,7 +17,7 @@ namespace UniGetUI.PackageEngine.PackageClasses /// public class InstallationOptions : IInstallationOptions { - private static readonly ConcurrentDictionary OptionsCache = []; + // private static readonly ConcurrentDictionary OptionsCache = []; public bool SkipHashCheck { get; set; } public bool InteractiveInstallation { get; set; } @@ -30,90 +30,105 @@ public class InstallationOptions : IInstallationOptions public bool RemoveDataOnUninstall { get; set; } public bool PreRelease { get; set; } public string CustomInstallLocation { get; set; } = ""; - - public IPackage Package { get; } + public bool OverridesNextLevelOpts { get; set; } private readonly string __save_filename; - private InstallationOptions(IPackage package) + private InstallationOptions(string filename) { - Package = package; - __save_filename = package.Manager.Name.Replace(" ", "").Replace(".", "") + "." + package.Id; + __save_filename = Path.Join(CoreData.UniGetUIInstallationOptionsDirectory, filename); } - /// - /// Returns the InstallationOptions object associated with the given package. - /// - /// The package's InstallationOptions instance - public static InstallationOptions FromPackage(IPackage package, bool? elevated = null, bool? - interactive = null, bool? no_integrity = null, bool? remove_data = null) + private static class StoragePath { - InstallationOptions instance; - if (OptionsCache.TryGetValue(package.GetHash(), out InstallationOptions? cached_instance) && cached_instance is not null) - { - instance = cached_instance; - } - else - { - instance = new(package); - instance.LoadFromDisk(); - OptionsCache.TryAdd(package.GetHash(), instance); - } + public static string Get(IPackageManager manager) + => "GlobalValues." + manager.Name.Replace(" ", "").Replace(".", ""); - if (elevated is not null) - { - instance.RunAsAdministrator = (bool)elevated; - } + public static string Get(IPackage package) + => package.Manager.Name.Replace(" ", "").Replace(".", "") + "." + package.Id; + } - if (interactive is not null) - { - instance.InteractiveInstallation = (bool)interactive; - } + public static InstallationOptions CreateEmpty(IPackage package) + { + var pkg_path = StoragePath.Get(package); + return new(pkg_path); + } - if (no_integrity is not null) - { - instance.SkipHashCheck = (bool)no_integrity; - } + public static InstallationOptions CreateEmpty(IPackageManager manager) + { + var mgr_path = StoragePath.Get(manager); + return new(mgr_path); + } - if (remove_data is not null) - { - instance.RemoveDataOnUninstall = (bool)remove_data; - } + public static InstallationOptions FromSerialized(SerializableInstallationOptions options, IPackage package) + { + var instance = CreateEmpty(package); + instance.GetValuesFromSerializable(options); + return instance; + } + public static InstallationOptions FromSerialized(SerializableInstallationOptions options, IPackageManager manager) + { + var instance = CreateEmpty(manager); + instance.GetValuesFromSerializable(options); return instance; } - /// - /// Returns the InstallationOptions object associated with the given package. - /// - /// The package from which to load the InstallationOptions - /// The package's InstallationOptions instance - public static async Task FromPackageAsync(IPackage package, bool? elevated = null, - bool? interactive = null, bool? no_integrity = null, bool? remove_data = null) + public static InstallationOptions LoadForPackage(IPackage package) { - return await Task.Run(() => FromPackage(package, elevated, interactive, no_integrity, remove_data)); + InstallationOptions instance = CreateEmpty(package); + instance.LoadFromDisk(); + return instance; } - /// - /// Returns a new InstallationOptions object from a given SerializableInstallationOptions and a package. - /// - public static InstallationOptions FromSerialized(SerializableInstallationOptions options, IPackage package) + public static async Task LoadForPackageAsync(IPackage package) { - InstallationOptions instance = new(package); - instance.FromSerializable(options); + InstallationOptions instance = CreateEmpty(package); + await Task.Run(instance.LoadFromDisk); return instance; } - public static InstallationOptions CreateEmpty(IPackage package) + public static InstallationOptions LoadForManager(IPackageManager manager) + { + InstallationOptions instance = CreateEmpty(manager); + instance.LoadFromDisk(); + return instance; + } + + public static InstallationOptions LoadApplicable( + IPackage package, + bool? elevated = null, + bool? interactive = null, + bool? no_integrity = null, + bool? remove_data = null) { - InstallationOptions instance = new(package); + InstallationOptions instance = LoadForPackage(package); + if (!instance.OverridesNextLevelOpts) + { + instance = LoadForManager(package.Manager); + Logger.Debug($"Package {package.Id} does not override options, will use package manager's default..."); + } + + if (elevated is not null) instance.RunAsAdministrator = (bool)elevated; + if (interactive is not null) instance.InteractiveInstallation = (bool)interactive; + if (no_integrity is not null) instance.SkipHashCheck = (bool)no_integrity; + if (remove_data is not null) instance.RemoveDataOnUninstall = (bool)remove_data; + return instance; } + public static Task LoadApplicableAsync( + IPackage package, + bool? elevated = null, + bool? interactive = null, + bool? no_integrity = null, + bool? remove_data = null) + => Task.Run(() => LoadApplicable(package, elevated, interactive, no_integrity, remove_data)); + /// /// Loads and applies the options from the given SerializableInstallationOptions object to the current object. /// - public void FromSerializable(SerializableInstallationOptions options) + public void GetValuesFromSerializable(SerializableInstallationOptions options) { SkipHashCheck = options.SkipHashCheck; InteractiveInstallation = options.InteractiveInstallation; @@ -122,24 +137,21 @@ public void FromSerializable(SerializableInstallationOptions options) Version = options.Version; SkipMinorUpdates = options.SkipMinorUpdates; PreRelease = options.PreRelease; + OverridesNextLevelOpts = options.OverridesNextLevelOpts; - if (options.Architecture != "" && CommonTranslations.InvertedArchNames.TryGetValue(options.Architecture, out var name)) + Architecture = null; + if (options.Architecture.Any() && + CommonTranslations.InvertedArchNames.TryGetValue(options.Architecture, out var name)) { Architecture = name; } - else - { - Architecture = null; - } - if (options.InstallationScope != "" && CommonTranslations.InvertedScopeNames_NonLang.TryGetValue(options.InstallationScope, out var value)) + InstallationScope = null; + if (options.InstallationScope.Any() && + CommonTranslations.InvertedScopeNames_NonLang.TryGetValue(options.InstallationScope, out var value)) { InstallationScope = value; } - else - { - InstallationScope = null; - } CustomParameters = options.CustomParameters; } @@ -147,7 +159,7 @@ public void FromSerializable(SerializableInstallationOptions options) /// /// Returns a SerializableInstallationOptions object containing the options of the current instance. /// - public SerializableInstallationOptions AsSerializable() + public SerializableInstallationOptions ToSerializable() { SerializableInstallationOptions options = new() { @@ -157,36 +169,22 @@ public SerializableInstallationOptions AsSerializable() CustomInstallLocation = CustomInstallLocation, PreRelease = PreRelease, Version = Version, - SkipMinorUpdates = SkipMinorUpdates + SkipMinorUpdates = SkipMinorUpdates, + OverridesNextLevelOpts = OverridesNextLevelOpts }; + if (Architecture is not null) - { options.Architecture = CommonTranslations.ArchNames[Architecture.Value]; - } if (InstallationScope is not null) - { options.InstallationScope = CommonTranslations.ScopeNames_NonLang[InstallationScope.Value]; - } options.CustomParameters = CustomParameters; return options; } - private FileInfo GetPackageOptionsFile() - { - string optionsFileName = Package.Manager.Name + "." + Package.Id.Split(":")[0] + ".json"; - return new FileInfo(Path.Join(CoreData.UniGetUIInstallationOptionsDirectory, optionsFileName)); - } - - /// - /// Saves the current options to disk, asynchronously. - /// - public async Task SaveToDiskAsync() - { - await Task.Run(SaveToDisk); - } + private static ConcurrentDictionary _optionsCache = new(); /// /// Saves the current options to disk. /// @@ -194,52 +192,66 @@ public void SaveToDisk() { try { - FileInfo optionsFile = GetPackageOptionsFile(); - if (optionsFile.Directory?.Exists == false) + var dir = Path.GetDirectoryName(__save_filename); + ArgumentException.ThrowIfNullOrEmpty(dir); + if (!Directory.Exists(dir)) { - optionsFile.Directory.Create(); + Directory.CreateDirectory(dir); } + var serialized = ToSerializable(); + _optionsCache[__save_filename] = serialized; + string fileContents = JsonSerializer.Serialize( - AsSerializable(), + serialized, SerializationHelpers.DefaultOptions ); - File.WriteAllText(optionsFile.FullName, fileContents); + File.WriteAllText(__save_filename, fileContents); } catch (Exception ex) { - Logger.Error($"Could not save {Package.Id} options to disk"); + Logger.Error($"Could not save {__save_filename} options to disk"); Logger.Error(ex); } } + /// + /// Saves the current options to disk. + /// + public Task SaveToDiskAsync() => Task.Run(SaveToDisk); + /// /// Loads the options from disk. /// - public void LoadFromDisk() + private void LoadFromDisk() { - FileInfo optionsFile = GetPackageOptionsFile(); try { - if (!optionsFile.Exists) - return; - - var rawData = File.ReadAllText(optionsFile.FullName); - JsonNode? jsonData = JsonNode.Parse(rawData); - if (jsonData is null) - return; + SerializableInstallationOptions serializedOptions; + if (_optionsCache.TryGetValue(__save_filename, out var cached)) + { + serializedOptions = cached; + } + else + { + if (!File.Exists(__save_filename)) return; - var serializedOptions = new SerializableInstallationOptions(jsonData); - FromSerializable(serializedOptions); + var rawData = File.ReadAllText(__save_filename); + JsonNode? jsonData = JsonNode.Parse(rawData); + ArgumentNullException.ThrowIfNull(jsonData); + serializedOptions = new SerializableInstallationOptions(jsonData); + _optionsCache[__save_filename] = serializedOptions; + } + GetValuesFromSerializable(serializedOptions); } catch (JsonException) { - Logger.Warn("An error occurred while parsing package " + optionsFile + ". The file will be overwritten"); - File.WriteAllText(optionsFile.FullName, "{}"); + Logger.Warn("An error occurred while parsing package " + __save_filename + ". The file will be overwritten"); + File.WriteAllText(__save_filename, "{}"); } catch (Exception e) { - Logger.Error("Loading installation options for file " + optionsFile + " have failed: "); + Logger.Error("Loading installation options for file " + __save_filename + " have failed: "); Logger.Error(e); } } @@ -249,7 +261,7 @@ public void LoadFromDisk() /// public override string ToString() { - string customparams = CustomParameters is not null ? string.Join(",", CustomParameters) : "[]"; + string customparams = CustomParameters.Any() ? string.Join(",", CustomParameters) : "[]"; return $" RegisterAndGetPackageAsync() { - var options = await InstallationOptions.FromPackageAsync(this); - options.FromSerializable(installation_options); - await options.SaveToDiskAsync(); + await Task.Run(() => + { + InstallationOptions.FromSerialized(installation_options, this).SaveToDisk(); + }); if (updates_options.UpdatesIgnored) { diff --git a/src/UniGetUI.PackageEngine.PackageManagerClasses/Packages/Package.cs b/src/UniGetUI.PackageEngine.PackageManagerClasses/Packages/Package.cs index 5f099f5709..56e54d2a7e 100644 --- a/src/UniGetUI.PackageEngine.PackageManagerClasses/Packages/Package.cs +++ b/src/UniGetUI.PackageEngine.PackageManagerClasses/Packages/Package.cs @@ -310,7 +310,7 @@ public virtual SerializablePackage AsSerializable() Version = VersionString, Source = Source.Name, ManagerName = Manager.Name, - InstallationOptions = InstallationOptions.FromPackage(this).AsSerializable(), + InstallationOptions = InstallationOptions.LoadForPackage(this).ToSerializable(), Updates = new SerializableUpdatesOptions { IgnoredVersion = GetIgnoredUpdatesVersionAsync().GetAwaiter().GetResult(), diff --git a/src/UniGetUI.PackageEngine.Serializable.Tests/TestSerializableInstallationOptions.cs b/src/UniGetUI.PackageEngine.Serializable.Tests/TestSerializableInstallationOptions.cs index dd02ab6692..d20cd968a4 100644 --- a/src/UniGetUI.PackageEngine.Serializable.Tests/TestSerializableInstallationOptions.cs +++ b/src/UniGetUI.PackageEngine.Serializable.Tests/TestSerializableInstallationOptions.cs @@ -45,7 +45,7 @@ public void ToAndFromJsonNode(bool a, bool b, string c, string d, string e, stri } [Theory] - [InlineData("{}", false, false, "", "", "", "", "", "", false, false, false, "")] + [InlineData("{}", false, false, "", "", "", "", "", "", false, false, false, "", false)] [InlineData(""" { "SkipHashCheck": true, @@ -57,7 +57,7 @@ public void ToAndFromJsonNode(bool a, bool b, string c, string d, string e, stri "a" ] } - """, true, true, "", "a", "", "", "", "lol", false, false, false, "")] + """, true, true, "", "a", "", "", "", "lol", false, false, false, "", true)] [InlineData(""" { @@ -71,8 +71,8 @@ public void ToAndFromJsonNode(bool a, bool b, string c, string d, string e, stri "UNKNOWN_VAL4": "hehe" } """, false, false, "", "", "", "", - "", "", false, false, true, "heyheyhey")] - public void FromJson(string JSON, bool hash, bool inter, string installLoc, string arg1, string arg2, string arg3, string scope, string arch, bool pre, bool admin, bool skipMin, string ver) + "", "", false, false, true, "heyheyhey", true)] + public void FromJson(string JSON, bool hash, bool inter, string installLoc, string arg1, string arg2, string arg3, string scope, string arch, bool pre, bool admin, bool skipMin, string ver, bool mod) { Assert.NotEmpty(JSON); var jsonContent = JsonNode.Parse(JSON); @@ -81,6 +81,8 @@ public void FromJson(string JSON, bool hash, bool inter, string installLoc, stri var list = new List() { arg1, arg2, arg3 }.Where(x => x.Any()); + Assert.Equal(mod, o2.OverridesNextLevelOpts); + Assert.Equal(mod, o2.DiffersFromDefault()); Assert.Equal(hash, o2.SkipHashCheck); Assert.Equal(arch, o2.Architecture); Assert.Equal(installLoc, o2.CustomInstallLocation); diff --git a/src/UniGetUI.PackageEngine.Serializable/SerializableInstallationOptions.cs b/src/UniGetUI.PackageEngine.Serializable/SerializableInstallationOptions.cs index 2b02e2363a..7a78272847 100644 --- a/src/UniGetUI.PackageEngine.Serializable/SerializableInstallationOptions.cs +++ b/src/UniGetUI.PackageEngine.Serializable/SerializableInstallationOptions.cs @@ -15,6 +15,7 @@ public class SerializableInstallationOptions: SerializableComponent() ?? ""; this.Version = data[nameof(Version)]?.GetVal() ?? ""; this.SkipMinorUpdates = data[nameof(SkipMinorUpdates)]?.GetVal() ?? false; + + // if OverridesNextLevelOpts is not found on the JSON, set it to true or false depending + // on whether the current settings instances are different from the default values. + // This entry shall be checked the last one, to ensure all other properties are set + this.OverridesNextLevelOpts = + data[nameof(OverridesNextLevelOpts)]?.GetValue() ?? DiffersFromDefault(); } public bool DiffersFromDefault() @@ -63,6 +71,8 @@ SkipMinorUpdates is not false || CustomParameters.Where(x => x != "").Any() || CustomInstallLocation.Any() || Version.Any(); + // OverridesNextLevelOpts does not need to be checked here, since + // this method is invoked before this property has been set } public SerializableInstallationOptions() : base() diff --git a/src/UniGetUI/AppOperationHelper.cs b/src/UniGetUI/AppOperationHelper.cs index 9b08b250fc..771072ec27 100644 --- a/src/UniGetUI/AppOperationHelper.cs +++ b/src/UniGetUI/AppOperationHelper.cs @@ -124,7 +124,7 @@ public static void Remove(AbstractOperation op) { if (package is null) return null; - var options = await InstallationOptions.FromPackageAsync(package, elevated, interactive, no_integrity); + var options = await InstallationOptions.LoadApplicableAsync(package, elevated, interactive, no_integrity); var op = new InstallPackageOperation(package, options, ignoreParallel, req); op.OperationSucceeded += (_, _) => TelemetryHandler.InstallPackage(package, TEL_OP_RESULT.SUCCESS, referral); op.OperationFailed += (_, _) => TelemetryHandler.InstallPackage(package, TEL_OP_RESULT.FAILED, referral); @@ -148,7 +148,7 @@ public static void Install(IReadOnlyList packages, TEL_InstallReferral { if (package is null) return null; - var options = await InstallationOptions.FromPackageAsync(package, elevated, interactive, no_integrity); + var options = await InstallationOptions.LoadApplicableAsync(package, elevated, interactive, no_integrity); var op = new UpdatePackageOperation(package, options, ignoreParallel, req); op.OperationSucceeded += (_, _) => TelemetryHandler.UpdatePackage(package, TEL_OP_RESULT.SUCCESS); op.OperationFailed += (_, _) => TelemetryHandler.UpdatePackage(package, TEL_OP_RESULT.FAILED); @@ -221,7 +221,7 @@ public static async void ConfirmAndUninstall(IPackage? package, bool? elevated = { if (package is null) return null; - var options = await InstallationOptions.FromPackageAsync(package, elevated, interactive, remove_data: remove_data); + var options = await InstallationOptions.LoadApplicableAsync(package, elevated, interactive, remove_data: remove_data); var op = new UninstallPackageOperation(package, options, ignoreParallel, req); op.OperationSucceeded += (_, _) => TelemetryHandler.UninstallPackage(package, TEL_OP_RESULT.SUCCESS); op.OperationFailed += (_, _) => TelemetryHandler.UninstallPackage(package, TEL_OP_RESULT.FAILED); diff --git a/src/UniGetUI/Pages/DialogPages/DialogHelper_Packages.cs b/src/UniGetUI/Pages/DialogPages/DialogHelper_Packages.cs index 2d22ef5a05..a7cad147c0 100644 --- a/src/UniGetUI/Pages/DialogPages/DialogHelper_Packages.cs +++ b/src/UniGetUI/Pages/DialogPages/DialogHelper_Packages.cs @@ -20,13 +20,13 @@ public static partial class DialogHelper /// public static async Task ShowInstallatOptions_Continue(IPackage package, OperationType operation) { - var options = (await InstallationOptions.FromPackageAsync(package)).AsSerializable(); + var options = (await InstallationOptions.LoadForPackageAsync(package)).ToSerializable(); var (dialogOptions, dialogResult) = await ShowInstallOptions(package, operation, options); if (dialogResult != ContentDialogResult.None) { - InstallationOptions newOptions = await InstallationOptions.FromPackageAsync(package); - newOptions.FromSerializable(dialogOptions); + InstallationOptions newOptions = await InstallationOptions.LoadForPackageAsync(package); + newOptions.GetValuesFromSerializable(dialogOptions); await newOptions.SaveToDiskAsync(); } @@ -68,7 +68,7 @@ public static async Task ShowInstallOptions_ImportedPackage }; OptionsDialog.PrimaryButtonText = CoreTools.Translate("Save and close"); OptionsDialog.DefaultButton = ContentDialogButton.Secondary; - OptionsDialog.Title = CoreTools.Translate("{0} installation options", package.Name); + // OptionsDialog.Title = CoreTools.Translate("{0} installation options", package.Name); OptionsDialog.Content = OptionsPage; OptionsPage.Close += (_, _) => { OptionsDialog.Hide(); }; diff --git a/src/UniGetUI/Pages/DialogPages/InstallOptions.xaml b/src/UniGetUI/Pages/DialogPages/InstallOptions.xaml deleted file mode 100644 index f899415471..0000000000 --- a/src/UniGetUI/Pages/DialogPages/InstallOptions.xaml +++ /dev/null @@ -1,231 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/UniGetUI/Pages/DialogPages/InstallOptions_Manager.xaml b/src/UniGetUI/Pages/DialogPages/InstallOptions_Manager.xaml new file mode 100644 index 0000000000..2fad9d1f16 --- /dev/null +++ b/src/UniGetUI/Pages/DialogPages/InstallOptions_Manager.xaml @@ -0,0 +1,176 @@ + + + + + + + + + + + + + + + + + + + +