From 8c136580e29e8f58acca7432ae131e47da8f5dbb Mon Sep 17 00:00:00 2001 From: Jeremy Wu Date: Tue, 8 Dec 2020 21:59:45 +1100 Subject: [PATCH] remove InstallPlugin method from API - do not allow InstallPlugin method to be called via API - move InstallPlugin functionality to PluginsManager for use exclusively --- Flow.Launcher.Core/Flow.Launcher.Core.csproj | 1 - Flow.Launcher.Core/Plugin/PluginInstaller.cs | 169 ------------------ Flow.Launcher.Core/Plugin/PluginManager.cs | 5 - Flow.Launcher.Plugin/IPublicAPI.cs | 6 - Flow.Launcher/PublicAPIInstance.cs | 5 - ...Flow.Launcher.Plugin.PluginsManager.csproj | 4 + .../PluginsManager.cs | 157 ++++++++++++++++ 7 files changed, 161 insertions(+), 186 deletions(-) delete mode 100644 Flow.Launcher.Core/Plugin/PluginInstaller.cs diff --git a/Flow.Launcher.Core/Flow.Launcher.Core.csproj b/Flow.Launcher.Core/Flow.Launcher.Core.csproj index 9f146a457..189a6669e 100644 --- a/Flow.Launcher.Core/Flow.Launcher.Core.csproj +++ b/Flow.Launcher.Core/Flow.Launcher.Core.csproj @@ -55,7 +55,6 @@ - diff --git a/Flow.Launcher.Core/Plugin/PluginInstaller.cs b/Flow.Launcher.Core/Plugin/PluginInstaller.cs deleted file mode 100644 index 7b980a3ee..000000000 --- a/Flow.Launcher.Core/Plugin/PluginInstaller.cs +++ /dev/null @@ -1,169 +0,0 @@ -using System; -using System.IO; -using System.Windows; -using ICSharpCode.SharpZipLib.Zip; -using Newtonsoft.Json; -using Flow.Launcher.Plugin; -using Flow.Launcher.Infrastructure; -using Flow.Launcher.Infrastructure.Logger; - -namespace Flow.Launcher.Core.Plugin -{ - internal class PluginInstaller - { - internal static void Install(string path) - { - if (File.Exists(path)) - { - string tempFolder = Path.Combine(Path.GetTempPath(), "flowlauncher", "plugins"); - if (Directory.Exists(tempFolder)) - { - Directory.Delete(tempFolder, true); - } - UnZip(path, tempFolder, true); - - string jsonPath = Path.Combine(tempFolder, Constant.PluginMetadataFileName); - if (!File.Exists(jsonPath)) - { - MessageBox.Show("Install failed: plugin config is missing"); - return; - } - - PluginMetadata plugin = GetMetadataFromJson(tempFolder); - if (plugin == null || plugin.Name == null) - { - MessageBox.Show("Install failed: plugin config is invalid"); - return; - } - - string pluginFolderPath = Infrastructure.UserSettings.DataLocation.PluginsDirectory; - - string newPluginName = plugin.Name - .Replace("/", "_") - .Replace("\\", "_") - .Replace(":", "_") - .Replace("<", "_") - .Replace(">", "_") - .Replace("?", "_") - .Replace("*", "_") - .Replace("|", "_") - + "-" + Guid.NewGuid(); - - string newPluginPath = Path.Combine(pluginFolderPath, newPluginName); - - string content = $"Do you want to install following plugin?{Environment.NewLine}{Environment.NewLine}" + - $"Name: {plugin.Name}{Environment.NewLine}" + - $"Version: {plugin.Version}{Environment.NewLine}" + - $"Author: {plugin.Author}"; - PluginPair existingPlugin = PluginManager.GetPluginForId(plugin.ID); - - if (existingPlugin != null) - { - content = $"Do you want to update following plugin?{Environment.NewLine}{Environment.NewLine}" + - $"Name: {plugin.Name}{Environment.NewLine}" + - $"Old Version: {existingPlugin.Metadata.Version}" + - $"{Environment.NewLine}New Version: {plugin.Version}" + - $"{Environment.NewLine}Author: {plugin.Author}"; - } - - var result = MessageBox.Show(content, "Install plugin", MessageBoxButton.YesNo, MessageBoxImage.Question); - if (result == MessageBoxResult.Yes) - { - if (existingPlugin != null && Directory.Exists(existingPlugin.Metadata.PluginDirectory)) - { - //when plugin is in use, we can't delete them. That's why we need to make plugin folder a random name - File.Create(Path.Combine(existingPlugin.Metadata.PluginDirectory, "NeedDelete.txt")).Close(); - } - - Directory.Move(tempFolder, newPluginPath); - - //exsiting plugins may be has loaded by application, - //if we try to delelte those kind of plugins, we will get a error that indicate the - //file is been used now. - //current solution is to restart Flow Launcher. Ugly. - //if (MainWindow.Initialized) - //{ - // Plugins.Initialize(); - //} - if (MessageBox.Show($"You have installed plugin {plugin.Name} successfully.{Environment.NewLine}" + - "Restart Flow Launcher to take effect?", - "Install plugin", MessageBoxButton.YesNo, MessageBoxImage.Question) == MessageBoxResult.Yes) - { - PluginManager.API.RestartApp(); - } - } - } - } - - private static PluginMetadata GetMetadataFromJson(string pluginDirectory) - { - string configPath = Path.Combine(pluginDirectory, Constant.PluginMetadataFileName); - PluginMetadata metadata; - - if (!File.Exists(configPath)) - { - return null; - } - - try - { - metadata = JsonConvert.DeserializeObject(File.ReadAllText(configPath)); - metadata.PluginDirectory = pluginDirectory; - } - catch (Exception e) - { - Log.Exception($"|PluginInstaller.GetMetadataFromJson|plugin config {configPath} failed: invalid json format", e); - return null; - } - - if (!AllowedLanguage.IsAllowed(metadata.Language)) - { - Log.Error($"|PluginInstaller.GetMetadataFromJson|plugin config {configPath} failed: invalid language {metadata.Language}"); - return null; - } - if (!File.Exists(metadata.ExecuteFilePath)) - { - Log.Error($"|PluginInstaller.GetMetadataFromJson|plugin config {configPath} failed: file {metadata.ExecuteFilePath} doesn't exist"); - return null; - } - - return metadata; - } - - /// - /// unzip plugin contents to the given directory. - /// - /// The path to the zip file. - /// The output directory. - /// overwirte - private static void UnZip(string zipFile, string strDirectory, bool overWrite) - { - if (strDirectory == "") - strDirectory = Directory.GetCurrentDirectory(); - - using (ZipInputStream zipStream = new ZipInputStream(File.OpenRead(zipFile))) - { - ZipEntry theEntry; - - while ((theEntry = zipStream.GetNextEntry()) != null) - { - var pathToZip = theEntry.Name; - var directoryName = String.IsNullOrEmpty(pathToZip) ? "" : Path.GetDirectoryName(pathToZip); - var fileName = Path.GetFileName(pathToZip); - var destinationDir = Path.Combine(strDirectory, directoryName); - var destinationFile = Path.Combine(destinationDir, fileName); - - Directory.CreateDirectory(destinationDir); - - if (String.IsNullOrEmpty(fileName) || (File.Exists(destinationFile) && !overWrite)) - continue; - - using (FileStream streamWriter = File.Create(destinationFile)) - { - zipStream.CopyTo(streamWriter); - } - } - } - } - } -} diff --git a/Flow.Launcher.Core/Plugin/PluginManager.cs b/Flow.Launcher.Core/Plugin/PluginManager.cs index 5cde9de83..3b697a1ee 100644 --- a/Flow.Launcher.Core/Plugin/PluginManager.cs +++ b/Flow.Launcher.Core/Plugin/PluginManager.cs @@ -133,11 +133,6 @@ namespace Flow.Launcher.Core.Plugin } } - public static void InstallPlugin(string path) - { - PluginInstaller.Install(path); - } - public static List ValidPluginsForQuery(Query query) { if (NonGlobalPlugins.ContainsKey(query.ActionKeyword)) diff --git a/Flow.Launcher.Plugin/IPublicAPI.cs b/Flow.Launcher.Plugin/IPublicAPI.cs index 681973905..ccc00d5e9 100644 --- a/Flow.Launcher.Plugin/IPublicAPI.cs +++ b/Flow.Launcher.Plugin/IPublicAPI.cs @@ -63,12 +63,6 @@ namespace Flow.Launcher.Plugin /// void OpenSettingDialog(); - /// - /// Install Flow Launcher plugin - /// - /// Plugin path (ends with .flowlauncher) - void InstallPlugin(string path); - /// /// Get translation of current language /// You need to implement IPluginI18n if you want to support multiple languages for your plugin diff --git a/Flow.Launcher/PublicAPIInstance.cs b/Flow.Launcher/PublicAPIInstance.cs index 0cc5a0e5d..90d4fff63 100644 --- a/Flow.Launcher/PublicAPIInstance.cs +++ b/Flow.Launcher/PublicAPIInstance.cs @@ -115,11 +115,6 @@ namespace Flow.Launcher _mainVM.ProgressBarVisibility = Visibility.Collapsed; } - public void InstallPlugin(string path) - { - Application.Current.Dispatcher.Invoke(() => PluginManager.InstallPlugin(path)); - } - public string GetTranslation(string key) { return InternationalizationManager.Instance.GetTranslation(key); diff --git a/Plugins/Flow.Launcher.Plugin.PluginManager/Flow.Launcher.Plugin.PluginsManager.csproj b/Plugins/Flow.Launcher.Plugin.PluginManager/Flow.Launcher.Plugin.PluginsManager.csproj index 84cc5ffb1..cc1a931ce 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginManager/Flow.Launcher.Plugin.PluginsManager.csproj +++ b/Plugins/Flow.Launcher.Plugin.PluginManager/Flow.Launcher.Plugin.PluginsManager.csproj @@ -39,4 +39,8 @@ PreserveNewest + + + + \ No newline at end of file diff --git a/Plugins/Flow.Launcher.Plugin.PluginManager/PluginsManager.cs b/Plugins/Flow.Launcher.Plugin.PluginManager/PluginsManager.cs index 6a9da4eea..5484fdf85 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginManager/PluginsManager.cs +++ b/Plugins/Flow.Launcher.Plugin.PluginManager/PluginsManager.cs @@ -3,6 +3,7 @@ using Flow.Launcher.Infrastructure.Http; using Flow.Launcher.Infrastructure.Logger; using Flow.Launcher.Infrastructure.UserSettings; using Flow.Launcher.Plugin.PluginsManager.Models; +using ICSharpCode.SharpZipLib.Zip; using System; using System.Collections.Generic; using System.IO; @@ -36,6 +37,7 @@ namespace Flow.Launcher.Plugin.PluginsManager var filePath = Path.Combine(DataLocation.PluginsDirectory, $"{plugin.Name}{plugin.ID}.zip"); PluginDownload(plugin.UrlDownload, filePath); + context.API.InstallPlugin(filePath); } private void PluginDownload(string downloadUrl, string toFilePath) @@ -105,5 +107,160 @@ namespace Flow.Launcher.Plugin.PluginsManager .Select(x => x) .ToList(); } + + private void Install(string path) + { + if (File.Exists(path)) + { + string tempFolder = Path.Combine(Path.GetTempPath(), "flowlauncher", "plugins"); + if (Directory.Exists(tempFolder)) + { + Directory.Delete(tempFolder, true); + } + UnZip(path, tempFolder, true); + + string jsonPath = Path.Combine(tempFolder, Constant.PluginMetadataFileName); + if (!File.Exists(jsonPath)) + { + MessageBox.Show("Install failed: plugin config is missing"); + return; + } + + PluginMetadata plugin = GetMetadataFromJson(tempFolder); + if (plugin == null || plugin.Name == null) + { + MessageBox.Show("Install failed: plugin config is invalid"); + return; + } + + string pluginFolderPath = Infrastructure.UserSettings.DataLocation.PluginsDirectory; + + string newPluginName = plugin.Name + .Replace("/", "_") + .Replace("\\", "_") + .Replace(":", "_") + .Replace("<", "_") + .Replace(">", "_") + .Replace("?", "_") + .Replace("*", "_") + .Replace("|", "_") + + "-" + Guid.NewGuid(); + + string newPluginPath = Path.Combine(pluginFolderPath, newPluginName); + + string content = $"Do you want to install following plugin?{Environment.NewLine}{Environment.NewLine}" + + $"Name: {plugin.Name}{Environment.NewLine}" + + $"Version: {plugin.Version}{Environment.NewLine}" + + $"Author: {plugin.Author}"; + PluginPair existingPlugin = PluginManager.GetPluginForId(plugin.ID); + + if (existingPlugin != null) + { + content = $"Do you want to update following plugin?{Environment.NewLine}{Environment.NewLine}" + + $"Name: {plugin.Name}{Environment.NewLine}" + + $"Old Version: {existingPlugin.Metadata.Version}" + + $"{Environment.NewLine}New Version: {plugin.Version}" + + $"{Environment.NewLine}Author: {plugin.Author}"; + } + + var result = MessageBox.Show(content, "Install plugin", MessageBoxButton.YesNo, MessageBoxImage.Question); + if (result == MessageBoxResult.Yes) + { + if (existingPlugin != null && Directory.Exists(existingPlugin.Metadata.PluginDirectory)) + { + //when plugin is in use, we can't delete them. That's why we need to make plugin folder a random name + File.Create(Path.Combine(existingPlugin.Metadata.PluginDirectory, "NeedDelete.txt")).Close(); + } + + Directory.Move(tempFolder, newPluginPath); + + //exsiting plugins may be has loaded by application, + //if we try to delelte those kind of plugins, we will get a error that indicate the + //file is been used now. + //current solution is to restart Flow Launcher. Ugly. + //if (MainWindow.Initialized) + //{ + // Plugins.Initialize(); + //} + if (MessageBox.Show($"You have installed plugin {plugin.Name} successfully.{Environment.NewLine}" + + "Restart Flow Launcher to take effect?", + "Install plugin", MessageBoxButton.YesNo, MessageBoxImage.Question) == MessageBoxResult.Yes) + { + PluginManager.API.RestartApp(); + } + } + } + } + + private PluginMetadata GetMetadataFromJson(string pluginDirectory) + { + string configPath = Path.Combine(pluginDirectory, Constant.PluginMetadataFileName); + PluginMetadata metadata; + + if (!File.Exists(configPath)) + { + return null; + } + + try + { + metadata = JsonConvert.DeserializeObject(File.ReadAllText(configPath)); + metadata.PluginDirectory = pluginDirectory; + } + catch (Exception e) + { + Log.Exception($"|PluginInstaller.GetMetadataFromJson|plugin config {configPath} failed: invalid json format", e); + return null; + } + + if (!AllowedLanguage.IsAllowed(metadata.Language)) + { + Log.Error($"|PluginInstaller.GetMetadataFromJson|plugin config {configPath} failed: invalid language {metadata.Language}"); + return null; + } + if (!File.Exists(metadata.ExecuteFilePath)) + { + Log.Error($"|PluginInstaller.GetMetadataFromJson|plugin config {configPath} failed: file {metadata.ExecuteFilePath} doesn't exist"); + return null; + } + + return metadata; + } + + /// + /// unzip plugin contents to the given directory. + /// + /// The path to the zip file. + /// The output directory. + /// overwirte + private void UnZip(string zipFile, string strDirectory, bool overWrite) + { + if (strDirectory == "") + strDirectory = Directory.GetCurrentDirectory(); + + using (ZipInputStream zipStream = new ZipInputStream(File.OpenRead(zipFile))) + { + ZipEntry theEntry; + + while ((theEntry = zipStream.GetNextEntry()) != null) + { + var pathToZip = theEntry.Name; + var directoryName = String.IsNullOrEmpty(pathToZip) ? "" : Path.GetDirectoryName(pathToZip); + var fileName = Path.GetFileName(pathToZip); + var destinationDir = Path.Combine(strDirectory, directoryName); + var destinationFile = Path.Combine(destinationDir, fileName); + + Directory.CreateDirectory(destinationDir); + + if (String.IsNullOrEmpty(fileName) || (File.Exists(destinationFile) && !overWrite)) + continue; + + using (FileStream streamWriter = File.Create(destinationFile)) + { + zipStream.CopyTo(streamWriter); + } + } + } + } } }