From 2e0db3b90f300926284763dc38e7d7005ceb9432 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Fri, 27 Feb 2026 18:30:29 +0800 Subject: [PATCH 01/10] Add MinimumAppVersion support for plugins Introduced MinimumAppVersion property to PluginMetadata, enabling plugins to specify required Flow Launcher version. Plugin installation now checks this requirement and prompts users if unsatisfied. Minimum app version logic moved to PluginManager and applied to manifest updates. Added localized strings for user prompts. Refactored SameOrLesserPluginVersionExists to accept PluginMetadata. --- .../ExternalPlugins/PluginsManifest.cs | 28 +---------- Flow.Launcher.Core/Plugin/PluginManager.cs | 50 ++++++++++++++++--- Flow.Launcher.Plugin/PluginMetadata.cs | 5 ++ Flow.Launcher/Languages/en.xaml | 2 + 4 files changed, 53 insertions(+), 32 deletions(-) diff --git a/Flow.Launcher.Core/ExternalPlugins/PluginsManifest.cs b/Flow.Launcher.Core/ExternalPlugins/PluginsManifest.cs index 4fed10d25ff..ba332d0a439 100644 --- a/Flow.Launcher.Core/ExternalPlugins/PluginsManifest.cs +++ b/Flow.Launcher.Core/ExternalPlugins/PluginsManifest.cs @@ -3,7 +3,7 @@ using System.Threading; using System.Threading.Tasks; using Flow.Launcher.Plugin; -using Flow.Launcher.Infrastructure; +using Flow.Launcher.Core.Plugin; namespace Flow.Launcher.Core.ExternalPlugins { @@ -41,11 +41,10 @@ public static async Task UpdateManifestAsync(bool usePrimaryUrlOnly = fals return false; var updatedPluginResults = new List(); - var appVersion = SemanticVersioning.Version.Parse(Constant.Version); for (int i = 0; i < results.Count; i++) { - if (IsMinimumAppVersionSatisfied(results[i], appVersion)) + if (PluginManager.IsMinimumAppVersionSatisfied(results[i].Name, results[i].MinimumAppVersion)) updatedPluginResults.Add(results[i]); } @@ -72,28 +71,5 @@ public static async Task UpdateManifestAsync(bool usePrimaryUrlOnly = fals return false; } - - private static bool IsMinimumAppVersionSatisfied(UserPlugin plugin, SemanticVersioning.Version appVersion) - { - if (string.IsNullOrEmpty(plugin.MinimumAppVersion)) - return true; - - try - { - if (appVersion >= SemanticVersioning.Version.Parse(plugin.MinimumAppVersion)) - return true; - } - catch (Exception e) - { - PublicApi.Instance.LogException(ClassName, $"Failed to parse the minimum app version {plugin.MinimumAppVersion} for plugin {plugin.Name}. " - + "Plugin excluded from manifest", e); - return false; - } - - PublicApi.Instance.LogInfo(ClassName, $"Plugin {plugin.Name} requires minimum Flow Launcher version {plugin.MinimumAppVersion}, " - + $"but current version is {Constant.Version}. Plugin excluded from manifest."); - - return false; - } } } diff --git a/Flow.Launcher.Core/Plugin/PluginManager.cs b/Flow.Launcher.Core/Plugin/PluginManager.cs index b808e2a7fbd..5e570665607 100644 --- a/Flow.Launcher.Core/Plugin/PluginManager.cs +++ b/Flow.Launcher.Core/Plugin/PluginManager.cs @@ -6,6 +6,7 @@ using System.Text.Json; using System.Threading; using System.Threading.Tasks; +using System.Windows; using Flow.Launcher.Core.ExternalPlugins; using Flow.Launcher.Core.Resource; using Flow.Launcher.Infrastructure; @@ -813,15 +814,13 @@ private static string GetContainingFolderPathAfterUnzip(string unzippedParentFol return string.Empty; } - private static bool SameOrLesserPluginVersionExists(string metadataPath) + private static bool SameOrLesserPluginVersionExists(PluginMetadata metadata) { - var newMetadata = JsonSerializer.Deserialize(File.ReadAllText(metadataPath)); - - if (!Version.TryParse(newMetadata.Version, out var newVersion)) + if (!Version.TryParse(metadata.Version, out var newVersion)) return true; // If version is not valid, we assume it is lesser than any existing version // Get all plugins even if initialization failed so that we can check if the plugin with the same ID exists - return GetAllInitializedPlugins(includeFailed: true).Any(x => x.Metadata.ID == newMetadata.ID + return GetAllInitializedPlugins(includeFailed: true).Any(x => x.Metadata.ID == metadata.ID && Version.TryParse(x.Metadata.Version, out var version) && newVersion <= version); } @@ -897,13 +896,27 @@ internal static bool InstallPlugin(UserPlugin plugin, string zipFilePath, bool c return false; } - if (SameOrLesserPluginVersionExists(metadataJsonFilePath)) + var newMetadata = JsonSerializer.Deserialize(File.ReadAllText(metadataJsonFilePath)); + + if (SameOrLesserPluginVersionExists(newMetadata)) { PublicApi.Instance.ShowMsgError(Localize.failedToInstallPluginTitle(plugin.Name), Localize.pluginExistAlreadyMessage()); return false; } + if (!IsMinimumAppVersionSatisfied(newMetadata.Name, newMetadata.MinimumAppVersion)) + { + // Ask users if they want to install the plugin that doesn't satisfy the minimum app version requirement + if (PublicApi.Instance.ShowMsgBox( + Localize.pluginMinimumAppVersionUnsatisfiedMessage(newMetadata.Name), + Localize.pluginMinimumAppVersionUnsatisfiedTitle(newMetadata.Name, newMetadata.MinimumAppVersion), + MessageBoxButton.YesNo) == MessageBoxResult.No) + { + return false; + } + } + var folderName = string.IsNullOrEmpty(plugin.Version) ? $"{plugin.Name}-{Guid.NewGuid()}" : $"{plugin.Name}-{plugin.Version}"; var defaultPluginIDs = new List @@ -1050,6 +1063,31 @@ internal static async Task UninstallPluginAsync(PluginMetadata plugin, boo return true; } + internal static bool IsMinimumAppVersionSatisfied(string pluginName, string minimumAppVersion) + { + if (string.IsNullOrEmpty(minimumAppVersion)) + return true; + + var appVersion = Version.Parse(Constant.Version); + + try + { + if (appVersion >= Version.Parse(minimumAppVersion)) + return true; + } + catch (Exception e) + { + PublicApi.Instance.LogException(ClassName, $"Failed to parse the minimum app version {minimumAppVersion} for plugin {pluginName}. " + + "Plugin excluded from manifest", e); + return false; + } + + PublicApi.Instance.LogInfo(ClassName, $"Plugin {pluginName} requires minimum Flow Launcher version {minimumAppVersion}, " + + $"but current version is {Constant.Version}. Plugin excluded from manifest."); + + return false; + } + #endregion #endregion diff --git a/Flow.Launcher.Plugin/PluginMetadata.cs b/Flow.Launcher.Plugin/PluginMetadata.cs index 09803cbd7cc..253573910db 100644 --- a/Flow.Launcher.Plugin/PluginMetadata.cs +++ b/Flow.Launcher.Plugin/PluginMetadata.cs @@ -137,6 +137,11 @@ internal set [JsonIgnore] public int QueryCount { get; set; } + /// + /// The minimum Flow Launcher version required for this plugin. Default is "". + /// + public string MinimumAppVersion { get; set; } = string.Empty; + /// /// The path to the plugin settings directory which is not validated. /// It is used to store plugin settings files and data files. diff --git a/Flow.Launcher/Languages/en.xaml b/Flow.Launcher/Languages/en.xaml index 145e1883dd0..a271ca48408 100644 --- a/Flow.Launcher/Languages/en.xaml +++ b/Flow.Launcher/Languages/en.xaml @@ -232,6 +232,8 @@ Unable to find plugin.json from the extracted zip file, or this path {0} does not exist A plugin with the same ID and version already exists, or the version is greater than this downloaded plugin Error creating setting panel for plugin {0}:{1}{2} + {0} requires Flow {1}+ version to run + Flow does not meet the minimum version requirements for {0} to run. Do you want to continue installing it? Note that you'd better update Flow to the latest version to ensure that {0} works without issues Plugin Store From 8e4f7258ffa44ab82bf3b25e05b2cb78a2b52ff3 Mon Sep 17 00:00:00 2001 From: Jack Ye Date: Fri, 27 Feb 2026 18:36:54 +0800 Subject: [PATCH 02/10] Improve translations Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- Flow.Launcher/Languages/en.xaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Flow.Launcher/Languages/en.xaml b/Flow.Launcher/Languages/en.xaml index a271ca48408..273c299504a 100644 --- a/Flow.Launcher/Languages/en.xaml +++ b/Flow.Launcher/Languages/en.xaml @@ -233,7 +233,7 @@ A plugin with the same ID and version already exists, or the version is greater than this downloaded plugin Error creating setting panel for plugin {0}:{1}{2} {0} requires Flow {1}+ version to run - Flow does not meet the minimum version requirements for {0} to run. Do you want to continue installing it? Note that you'd better update Flow to the latest version to ensure that {0} works without issues + Flow does not meet the minimum version requirements for {0} to run. Do you want to continue installing it? We recommend updating Flow to the latest version to ensure that {0} works without issues. Plugin Store From b1b18ee2154a3d1e7917f594fca77141b6319987 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Fri, 27 Feb 2026 18:42:07 +0800 Subject: [PATCH 03/10] Improve error handling for invalid plugin.json files Previously, installing a plugin with an invalid or corrupted plugin.json would cause an unhandled exception. Now, deserialization errors are caught, a user-friendly error message is shown, and the exception is logged. Added a new localized error message for this scenario in en.xaml. --- Flow.Launcher.Core/Plugin/PluginManager.cs | 14 +++++++++++++- Flow.Launcher/Languages/en.xaml | 1 + 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/Flow.Launcher.Core/Plugin/PluginManager.cs b/Flow.Launcher.Core/Plugin/PluginManager.cs index 5e570665607..e4972453f9e 100644 --- a/Flow.Launcher.Core/Plugin/PluginManager.cs +++ b/Flow.Launcher.Core/Plugin/PluginManager.cs @@ -896,7 +896,19 @@ internal static bool InstallPlugin(UserPlugin plugin, string zipFilePath, bool c return false; } - var newMetadata = JsonSerializer.Deserialize(File.ReadAllText(metadataJsonFilePath)); + PluginMetadata newMetadata; + try + { + newMetadata = JsonSerializer.Deserialize(File.ReadAllText(metadataJsonFilePath)); + } + catch (Exception ex) + { + PublicApi.Instance.ShowMsgError(Localize.failedToInstallPluginTitle(plugin.Name), + Localize.pluginJsonInvalidOrCorrupted()); + PublicApi.Instance.LogException(ClassName, + $"Failed to deserialize plugin metadata for plugin {plugin.Name} from file {metadataJsonFilePath}", ex); + return false; + } if (SameOrLesserPluginVersionExists(newMetadata)) { diff --git a/Flow.Launcher/Languages/en.xaml b/Flow.Launcher/Languages/en.xaml index 273c299504a..26c9d35e08a 100644 --- a/Flow.Launcher/Languages/en.xaml +++ b/Flow.Launcher/Languages/en.xaml @@ -234,6 +234,7 @@ Error creating setting panel for plugin {0}:{1}{2} {0} requires Flow {1}+ version to run Flow does not meet the minimum version requirements for {0} to run. Do you want to continue installing it? We recommend updating Flow to the latest version to ensure that {0} works without issues. + Failed to install plugin because plugin.json is invalid or corrupted Plugin Store From b02a5a265d0442dd8b06374e75b7b4d612408224 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Fri, 27 Feb 2026 18:48:59 +0800 Subject: [PATCH 04/10] Refactor plugin min version check and improve logging Refactored the plugin minimum app version check to use Version.TryParse instead of try-catch with Version.Parse, preventing exceptions on invalid input. Improved error handling by logging parse failures as errors and version mismatches as info, making the logic clearer and more robust. --- Flow.Launcher.Core/Plugin/PluginManager.cs | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/Flow.Launcher.Core/Plugin/PluginManager.cs b/Flow.Launcher.Core/Plugin/PluginManager.cs index e4972453f9e..f78b5936051 100644 --- a/Flow.Launcher.Core/Plugin/PluginManager.cs +++ b/Flow.Launcher.Core/Plugin/PluginManager.cs @@ -1082,18 +1082,17 @@ internal static bool IsMinimumAppVersionSatisfied(string pluginName, string mini var appVersion = Version.Parse(Constant.Version); - try - { - if (appVersion >= Version.Parse(minimumAppVersion)) - return true; - } - catch (Exception e) + if (!Version.TryParse(minimumAppVersion, out var minimumVersion)) { - PublicApi.Instance.LogException(ClassName, $"Failed to parse the minimum app version {minimumAppVersion} for plugin {pluginName}. " - + "Plugin excluded from manifest", e); - return false; + PublicApi.Instance.LogError(ClassName, + $"Failed to parse the minimum app version {minimumAppVersion} for plugin {pluginName}. " + + "Plugin excluded from manifest"); + return false; // If the minimum app version specified in plugin.json is invalid, we assume it is not satisfied } + if (appVersion >= minimumVersion) + return true; + PublicApi.Instance.LogInfo(ClassName, $"Plugin {pluginName} requires minimum Flow Launcher version {minimumAppVersion}, " + $"but current version is {Constant.Version}. Plugin excluded from manifest."); From 590bf20204a969d595ecfa88556bbbd9a45c3c03 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Fri, 27 Feb 2026 18:53:30 +0800 Subject: [PATCH 05/10] Add null check for plugin metadata deserialization Throw a JsonException if deserializing plugin metadata from JSON returns null. This prevents null metadata from being used and improves error handling for invalid or corrupted plugin.json files. --- Flow.Launcher.Core/Plugin/PluginManager.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Flow.Launcher.Core/Plugin/PluginManager.cs b/Flow.Launcher.Core/Plugin/PluginManager.cs index f78b5936051..36c63ab938f 100644 --- a/Flow.Launcher.Core/Plugin/PluginManager.cs +++ b/Flow.Launcher.Core/Plugin/PluginManager.cs @@ -899,7 +899,8 @@ internal static bool InstallPlugin(UserPlugin plugin, string zipFilePath, bool c PluginMetadata newMetadata; try { - newMetadata = JsonSerializer.Deserialize(File.ReadAllText(metadataJsonFilePath)); + newMetadata = JsonSerializer.Deserialize(File.ReadAllText(metadataJsonFilePath)) ?? + throw new JsonException("Deserialized metadata is null"); } catch (Exception ex) { From ba1908d605ac7e4e312f4874b23f2ad0c9113915 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Fri, 27 Feb 2026 18:54:13 +0800 Subject: [PATCH 06/10] Update plugin min version warning message formatting Improved the message shown when a plugin requires a newer Flow Launcher version by adding line breaks for clarity and updating the title wording. Adjusted code to pass Environment.NewLine and modified resource strings in en.xaml to support the new format. --- Flow.Launcher.Core/Plugin/PluginManager.cs | 4 ++-- Flow.Launcher/Languages/en.xaml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Flow.Launcher.Core/Plugin/PluginManager.cs b/Flow.Launcher.Core/Plugin/PluginManager.cs index 36c63ab938f..eaeab01b890 100644 --- a/Flow.Launcher.Core/Plugin/PluginManager.cs +++ b/Flow.Launcher.Core/Plugin/PluginManager.cs @@ -922,7 +922,7 @@ internal static bool InstallPlugin(UserPlugin plugin, string zipFilePath, bool c { // Ask users if they want to install the plugin that doesn't satisfy the minimum app version requirement if (PublicApi.Instance.ShowMsgBox( - Localize.pluginMinimumAppVersionUnsatisfiedMessage(newMetadata.Name), + Localize.pluginMinimumAppVersionUnsatisfiedMessage(newMetadata.Name, Environment.NewLine), Localize.pluginMinimumAppVersionUnsatisfiedTitle(newMetadata.Name, newMetadata.MinimumAppVersion), MessageBoxButton.YesNo) == MessageBoxResult.No) { @@ -1092,7 +1092,7 @@ internal static bool IsMinimumAppVersionSatisfied(string pluginName, string mini } if (appVersion >= minimumVersion) - return true; + return true; PublicApi.Instance.LogInfo(ClassName, $"Plugin {pluginName} requires minimum Flow Launcher version {minimumAppVersion}, " + $"but current version is {Constant.Version}. Plugin excluded from manifest."); diff --git a/Flow.Launcher/Languages/en.xaml b/Flow.Launcher/Languages/en.xaml index 26c9d35e08a..9d9d66f7390 100644 --- a/Flow.Launcher/Languages/en.xaml +++ b/Flow.Launcher/Languages/en.xaml @@ -232,8 +232,8 @@ Unable to find plugin.json from the extracted zip file, or this path {0} does not exist A plugin with the same ID and version already exists, or the version is greater than this downloaded plugin Error creating setting panel for plugin {0}:{1}{2} - {0} requires Flow {1}+ version to run - Flow does not meet the minimum version requirements for {0} to run. Do you want to continue installing it? We recommend updating Flow to the latest version to ensure that {0} works without issues. + {0} requires Flow {1} version to run + Flow does not meet the minimum version requirements for {0} to run. Do you want to continue installing it?{1}{1}We recommend updating Flow to the latest version to ensure that {0} works without issues. Failed to install plugin because plugin.json is invalid or corrupted From dca90916fe41ef290baffc37616430c4cd7febae Mon Sep 17 00:00:00 2001 From: Jack Ye Date: Fri, 27 Feb 2026 19:02:50 +0800 Subject: [PATCH 07/10] Fix translations Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- Flow.Launcher/Languages/en.xaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Flow.Launcher/Languages/en.xaml b/Flow.Launcher/Languages/en.xaml index 9d9d66f7390..1b2b01ef892 100644 --- a/Flow.Launcher/Languages/en.xaml +++ b/Flow.Launcher/Languages/en.xaml @@ -232,7 +232,7 @@ Unable to find plugin.json from the extracted zip file, or this path {0} does not exist A plugin with the same ID and version already exists, or the version is greater than this downloaded plugin Error creating setting panel for plugin {0}:{1}{2} - {0} requires Flow {1} version to run + {0} requires Flow version {1} to run Flow does not meet the minimum version requirements for {0} to run. Do you want to continue installing it?{1}{1}We recommend updating Flow to the latest version to ensure that {0} works without issues. Failed to install plugin because plugin.json is invalid or corrupted From 48878d2d8c785ac8a7672dc951b8aac261afc445 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Fri, 27 Feb 2026 19:04:30 +0800 Subject: [PATCH 08/10] Make log message context-neutral --- Flow.Launcher.Core/Plugin/PluginManager.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Flow.Launcher.Core/Plugin/PluginManager.cs b/Flow.Launcher.Core/Plugin/PluginManager.cs index eaeab01b890..b2eeac9a54c 100644 --- a/Flow.Launcher.Core/Plugin/PluginManager.cs +++ b/Flow.Launcher.Core/Plugin/PluginManager.cs @@ -1086,16 +1086,16 @@ internal static bool IsMinimumAppVersionSatisfied(string pluginName, string mini if (!Version.TryParse(minimumAppVersion, out var minimumVersion)) { PublicApi.Instance.LogError(ClassName, - $"Failed to parse the minimum app version {minimumAppVersion} for plugin {pluginName}. " - + "Plugin excluded from manifest"); + $"Failed to parse the minimum app version {minimumAppVersion} for plugin {pluginName}."); return false; // If the minimum app version specified in plugin.json is invalid, we assume it is not satisfied } if (appVersion >= minimumVersion) return true; - PublicApi.Instance.LogInfo(ClassName, $"Plugin {pluginName} requires minimum Flow Launcher version {minimumAppVersion}, " - + $"but current version is {Constant.Version}. Plugin excluded from manifest."); + PublicApi.Instance.LogInfo(ClassName, + $"Plugin {pluginName} requires minimum Flow Launcher version {minimumAppVersion}, " + + $"but current version is {Constant.Version}."); return false; } From 036a3761eae5ae3ffed41e1a0ef5636fd96e8df5 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Fri, 27 Feb 2026 19:05:25 +0800 Subject: [PATCH 09/10] Remove log for plugin version mismatch in PluginManager Logging when a plugin's minimum Flow Launcher version is not met has been removed. The method now returns false without logging any informational message. --- Flow.Launcher.Core/Plugin/PluginManager.cs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/Flow.Launcher.Core/Plugin/PluginManager.cs b/Flow.Launcher.Core/Plugin/PluginManager.cs index b2eeac9a54c..e11cae26909 100644 --- a/Flow.Launcher.Core/Plugin/PluginManager.cs +++ b/Flow.Launcher.Core/Plugin/PluginManager.cs @@ -1078,6 +1078,7 @@ internal static async Task UninstallPluginAsync(PluginMetadata plugin, boo internal static bool IsMinimumAppVersionSatisfied(string pluginName, string minimumAppVersion) { + // If the minimum app version is not specified in plugin.json, this plugin is compatible with all app versions if (string.IsNullOrEmpty(minimumAppVersion)) return true; @@ -1093,10 +1094,6 @@ internal static bool IsMinimumAppVersionSatisfied(string pluginName, string mini if (appVersion >= minimumVersion) return true; - PublicApi.Instance.LogInfo(ClassName, - $"Plugin {pluginName} requires minimum Flow Launcher version {minimumAppVersion}, " - + $"but current version is {Constant.Version}."); - return false; } From cce80ac5aacfe726c8af17fbd31cd307fecfa3a8 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Fri, 27 Feb 2026 19:14:03 +0800 Subject: [PATCH 10/10] Ensure temporary folder deletion --- Flow.Launcher.Core/Plugin/PluginManager.cs | 179 +++++++++++---------- 1 file changed, 92 insertions(+), 87 deletions(-) diff --git a/Flow.Launcher.Core/Plugin/PluginManager.cs b/Flow.Launcher.Core/Plugin/PluginManager.cs index e11cae26909..78464ddc9ba 100644 --- a/Flow.Launcher.Core/Plugin/PluginManager.cs +++ b/Flow.Launcher.Core/Plugin/PluginManager.cs @@ -880,111 +880,116 @@ internal static bool InstallPlugin(UserPlugin plugin, string zipFilePath, bool c var tempFolderPluginPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); System.IO.Compression.ZipFile.ExtractToDirectory(zipFilePath, tempFolderPluginPath); - if(!plugin.IsFromLocalInstallPath) - File.Delete(zipFilePath); - - var pluginFolderPath = GetContainingFolderPathAfterUnzip(tempFolderPluginPath); - - var metadataJsonFilePath = string.Empty; - if (File.Exists(Path.Combine(pluginFolderPath, Constant.PluginMetadataFileName))) - metadataJsonFilePath = Path.Combine(pluginFolderPath, Constant.PluginMetadataFileName); - - if (string.IsNullOrEmpty(metadataJsonFilePath) || string.IsNullOrEmpty(pluginFolderPath)) - { - PublicApi.Instance.ShowMsgError(Localize.failedToInstallPluginTitle(plugin.Name), - Localize.fileNotFoundMessage(pluginFolderPath)); - return false; - } - - PluginMetadata newMetadata; try { - newMetadata = JsonSerializer.Deserialize(File.ReadAllText(metadataJsonFilePath)) ?? - throw new JsonException("Deserialized metadata is null"); - } - catch (Exception ex) - { - PublicApi.Instance.ShowMsgError(Localize.failedToInstallPluginTitle(plugin.Name), - Localize.pluginJsonInvalidOrCorrupted()); - PublicApi.Instance.LogException(ClassName, - $"Failed to deserialize plugin metadata for plugin {plugin.Name} from file {metadataJsonFilePath}", ex); - return false; - } + if (!plugin.IsFromLocalInstallPath) + File.Delete(zipFilePath); - if (SameOrLesserPluginVersionExists(newMetadata)) - { - PublicApi.Instance.ShowMsgError(Localize.failedToInstallPluginTitle(plugin.Name), - Localize.pluginExistAlreadyMessage()); - return false; - } + var pluginFolderPath = GetContainingFolderPathAfterUnzip(tempFolderPluginPath); - if (!IsMinimumAppVersionSatisfied(newMetadata.Name, newMetadata.MinimumAppVersion)) - { - // Ask users if they want to install the plugin that doesn't satisfy the minimum app version requirement - if (PublicApi.Instance.ShowMsgBox( - Localize.pluginMinimumAppVersionUnsatisfiedMessage(newMetadata.Name, Environment.NewLine), - Localize.pluginMinimumAppVersionUnsatisfiedTitle(newMetadata.Name, newMetadata.MinimumAppVersion), - MessageBoxButton.YesNo) == MessageBoxResult.No) + var metadataJsonFilePath = string.Empty; + if (File.Exists(Path.Combine(pluginFolderPath, Constant.PluginMetadataFileName))) + metadataJsonFilePath = Path.Combine(pluginFolderPath, Constant.PluginMetadataFileName); + + if (string.IsNullOrEmpty(metadataJsonFilePath) || string.IsNullOrEmpty(pluginFolderPath)) { + PublicApi.Instance.ShowMsgError(Localize.failedToInstallPluginTitle(plugin.Name), + Localize.fileNotFoundMessage(pluginFolderPath)); return false; } - } - var folderName = string.IsNullOrEmpty(plugin.Version) ? $"{plugin.Name}-{Guid.NewGuid()}" : $"{plugin.Name}-{plugin.Version}"; + PluginMetadata newMetadata; + try + { + newMetadata = JsonSerializer.Deserialize(File.ReadAllText(metadataJsonFilePath)) ?? + throw new JsonException("Deserialized metadata is null"); + } + catch (Exception ex) + { + PublicApi.Instance.ShowMsgError(Localize.failedToInstallPluginTitle(plugin.Name), + Localize.pluginJsonInvalidOrCorrupted()); + PublicApi.Instance.LogException(ClassName, + $"Failed to deserialize plugin metadata for plugin {plugin.Name} from file {metadataJsonFilePath}", ex); + return false; + } - var defaultPluginIDs = new List + if (SameOrLesserPluginVersionExists(newMetadata)) { - "0ECADE17459B49F587BF81DC3A125110", // BrowserBookmark - "CEA0FDFC6D3B4085823D60DC76F28855", // Calculator - "572be03c74c642baae319fc283e561a8", // Explorer - "6A122269676E40EB86EB543B945932B9", // PluginIndicator - "9f8f9b14-2518-4907-b211-35ab6290dee7", // PluginsManager - "b64d0a79-329a-48b0-b53f-d658318a1bf6", // ProcessKiller - "791FC278BA414111B8D1886DFE447410", // Program - "D409510CD0D2481F853690A07E6DC426", // Shell - "CEA08895D2544B019B2E9C5009600DF4", // Sys - "0308FD86DE0A4DEE8D62B9B535370992", // URL - "565B73353DBF4806919830B9202EE3BF", // WebSearch - "5043CETYU6A748679OPA02D27D99677A" // WindowsSettings - }; + PublicApi.Instance.ShowMsgError(Localize.failedToInstallPluginTitle(plugin.Name), + Localize.pluginExistAlreadyMessage()); + return false; + } - // Treat default plugin differently, it needs to be removable along with each flow release - var installDirectory = !defaultPluginIDs.Any(x => x == plugin.ID) - ? DataLocation.PluginsDirectory - : Constant.PreinstalledDirectory; + if (!IsMinimumAppVersionSatisfied(newMetadata.Name, newMetadata.MinimumAppVersion)) + { + // Ask users if they want to install the plugin that doesn't satisfy the minimum app version requirement + if (PublicApi.Instance.ShowMsgBox( + Localize.pluginMinimumAppVersionUnsatisfiedMessage(newMetadata.Name, Environment.NewLine), + Localize.pluginMinimumAppVersionUnsatisfiedTitle(newMetadata.Name, newMetadata.MinimumAppVersion), + MessageBoxButton.YesNo) == MessageBoxResult.No) + { + return false; + } + } - var newPluginPath = Path.Combine(installDirectory, folderName); + var folderName = string.IsNullOrEmpty(plugin.Version) ? $"{plugin.Name}-{Guid.NewGuid()}" : $"{plugin.Name}-{plugin.Version}"; - FilesFolders.CopyAll(pluginFolderPath, newPluginPath, (s) => PublicApi.Instance.ShowMsgBox(s)); + var defaultPluginIDs = new List + { + "0ECADE17459B49F587BF81DC3A125110", // BrowserBookmark + "CEA0FDFC6D3B4085823D60DC76F28855", // Calculator + "572be03c74c642baae319fc283e561a8", // Explorer + "6A122269676E40EB86EB543B945932B9", // PluginIndicator + "9f8f9b14-2518-4907-b211-35ab6290dee7", // PluginsManager + "b64d0a79-329a-48b0-b53f-d658318a1bf6", // ProcessKiller + "791FC278BA414111B8D1886DFE447410", // Program + "D409510CD0D2481F853690A07E6DC426", // Shell + "CEA08895D2544B019B2E9C5009600DF4", // Sys + "0308FD86DE0A4DEE8D62B9B535370992", // URL + "565B73353DBF4806919830B9202EE3BF", // WebSearch + "5043CETYU6A748679OPA02D27D99677A" // WindowsSettings + }; + + // Treat default plugin differently, it needs to be removable along with each flow release + var installDirectory = !defaultPluginIDs.Any(x => x == plugin.ID) + ? DataLocation.PluginsDirectory + : Constant.PreinstalledDirectory; + + var newPluginPath = Path.Combine(installDirectory, folderName); + + FilesFolders.CopyAll(pluginFolderPath, newPluginPath, (s) => PublicApi.Instance.ShowMsgBox(s)); + + // Check if marker file exists and delete it + try + { + var markerFilePath = Path.Combine(newPluginPath, DataLocation.PluginDeleteFile); + if (File.Exists(markerFilePath)) + File.Delete(markerFilePath); + } + catch (Exception e) + { + PublicApi.Instance.LogException(ClassName, $"Failed to delete plugin marker file in {newPluginPath}", e); + } - // Check if marker file exists and delete it - try - { - var markerFilePath = Path.Combine(newPluginPath, DataLocation.PluginDeleteFile); - if (File.Exists(markerFilePath)) - File.Delete(markerFilePath); - } - catch (Exception e) - { - PublicApi.Instance.LogException(ClassName, $"Failed to delete plugin marker file in {newPluginPath}", e); - } + if (checkModified) + { + ModifiedPlugins.Add(plugin.ID); + } - try - { - if (Directory.Exists(tempFolderPluginPath)) - Directory.Delete(tempFolderPluginPath, true); - } - catch (Exception e) - { - PublicApi.Instance.LogException(ClassName, $"Failed to delete temp folder {tempFolderPluginPath}", e); + return true; } - - if (checkModified) + finally { - ModifiedPlugins.Add(plugin.ID); + try + { + if (Directory.Exists(tempFolderPluginPath)) + Directory.Delete(tempFolderPluginPath, true); + } + catch (Exception e) + { + PublicApi.Instance.LogException(ClassName, $"Failed to delete temp folder {tempFolderPluginPath}", e); + } } - - return true; } internal static async Task UninstallPluginAsync(PluginMetadata plugin, bool removePluginFromSettings, bool removePluginSettings, bool checkModified)