diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/en.xaml b/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/en.xaml index 89ed9acf3..a30b8e966 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/en.xaml +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/en.xaml @@ -10,6 +10,7 @@ {0} by {1} {2}{3}Would you like to uninstall this plugin? After the uninstallation Flow will automatically restart. {0} by {1} {2}{3}Would you like to install this plugin? After the installation Flow will automatically restart. Plugin Install + Download and install {0} Plugin Uninstall Plugin installation in progress. Please wait... Plugin successfully installed. Restarting Flow, please wait... @@ -25,7 +26,9 @@ This plugin is already installed Plugin Manifest Download Failed Please check if you can connect to github.com. This error means you may not be able to install or update plugins. - + Installing from an unknown source + You are installing this plugin from an unknown source and it may contain potential risks!{0}{0}Please ensure you understand where this plugin is from and that it is safe.{0}{0}Would you like to continue still?{0}{0}(You can switch off this warning via settings) + @@ -42,5 +45,7 @@ Suggest an enhancement or submit an issue to the plugin developer Go to Flow's plugins repository Visit the PluginsManifest repository to see community-made plugin submissions - + + + Install from unknown source warning \ No newline at end of file diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs b/Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs index 89a8e0ff5..f445826fa 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs @@ -55,7 +55,7 @@ namespace Flow.Launcher.Plugin.PluginsManager public async Task> QueryAsync(Query query, CancellationToken token) { - var search = query.Search.ToLower(); + var search = query.Search; if (string.IsNullOrWhiteSpace(search)) return pluginManager.GetDefaultHotKeys(); @@ -70,9 +70,13 @@ namespace Flow.Launcher.Plugin.PluginsManager return search switch { - var s when s.StartsWith(Settings.HotKeyInstall) => await pluginManager.RequestInstallOrUpdate(s, token), - var s when s.StartsWith(Settings.HotkeyUninstall) => pluginManager.RequestUninstall(s), - var s when s.StartsWith(Settings.HotkeyUpdate) => await pluginManager.RequestUpdate(s, token), + //search could be url, no need ToLower() when passed in + var s when s.StartsWith(Settings.HotKeyInstall, StringComparison.OrdinalIgnoreCase) + => await pluginManager.RequestInstallOrUpdate(search, token), + var s when s.StartsWith(Settings.HotkeyUninstall, StringComparison.OrdinalIgnoreCase) + => pluginManager.RequestUninstall(search), + var s when s.StartsWith(Settings.HotkeyUpdate, StringComparison.OrdinalIgnoreCase) + => await pluginManager.RequestUpdate(search, token), _ => pluginManager.GetDefaultHotKeys().Where(hotkey => { hotkey.Score = StringMatcher.FuzzySearch(search, hotkey.Title).Score; diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs b/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs index adc4beb6c..f37dd86ea 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs @@ -166,17 +166,12 @@ namespace Flow.Launcher.Plugin.PluginsManager catch (Exception e) { if (e is HttpRequestException) - { MessageBox.Show(Context.API.GetTranslation("plugin_pluginsmanager_download_error"), Context.API.GetTranslation("plugin_pluginsmanager_downloading_plugin")); - } - else - { - Context.API.ShowMsgError(Context.API.GetTranslation("plugin_pluginsmanager_install_error_title"), + Context.API.ShowMsgError(Context.API.GetTranslation("plugin_pluginsmanager_install_error_title"), string.Format(Context.API.GetTranslation("plugin_pluginsmanager_install_error_subtitle"), plugin.Name)); - } Log.Exception("PluginsManager", "An error occured while downloading plugin", e, "InstallOrUpdate"); @@ -206,7 +201,7 @@ namespace Flow.Launcher.Plugin.PluginsManager if (autocompletedResults.Any()) return autocompletedResults; - var uninstallSearch = search.Replace(Settings.HotkeyUpdate, string.Empty).TrimStart(); + var uninstallSearch = search.Replace(Settings.HotkeyUpdate, string.Empty, StringComparison.OrdinalIgnoreCase).TrimStart(); var resultsForUpdate = from existingPlugin in Context.API.GetAllPlugins() @@ -341,26 +336,46 @@ namespace Flow.Launcher.Plugin.PluginsManager var result = new Result { - Title = filename, - SubTitle = $"Download and Install from URL", + Title = string.Format(Context.API.GetTranslation("plugin_pluginsmanager_install_from_web"), filename), + SubTitle = plugin.UrlDownload, IcoPath = icoPath, Action = e => { if (e.SpecialKeyState.CtrlPressed) { - SearchWeb.NewTabInBrowser(url); + SearchWeb.NewTabInBrowser(plugin.UrlDownload); return ShouldHideWindow; } + if (Settings.WarnFromUnknownSource) + { + if (!InstallSourceKnown(plugin.UrlDownload) + && MessageBox.Show(string.Format(Context.API.GetTranslation("plugin_pluginsmanager_install_unknown_source_warning"), + Environment.NewLine), + Context.API.GetTranslation("plugin_pluginsmanager_install_unknown_source_warning_title"), + MessageBoxButton.YesNo) == MessageBoxResult.No) + return false; + } + Application.Current.MainWindow.Hide(); _ = InstallOrUpdate(plugin); + return ShouldHideWindow; } }; return new List { result }; } - + + private bool InstallSourceKnown(string url) + { + var author = url.Split('/')[3]; + var acceptedSource = "https://github.com"; + var contructedUrlPart = string.Format("{0}/{1}/", acceptedSource, author); + + return url.StartsWith(acceptedSource) && Context.API.GetAllPlugins().Any(x => x.Metadata.Website.StartsWith(contructedUrlPart)); + } + internal async ValueTask> RequestInstallOrUpdate(string searchName, CancellationToken token) { if (!PluginsManifest.UserPlugins.Any()) @@ -370,7 +385,7 @@ namespace Flow.Launcher.Plugin.PluginsManager token.ThrowIfCancellationRequested(); - var searchNameWithoutKeyword = searchName.Replace(Settings.HotKeyInstall, string.Empty).Trim(); + var searchNameWithoutKeyword = searchName.Replace(Settings.HotKeyInstall, string.Empty, StringComparison.OrdinalIgnoreCase).Trim(); if (Uri.IsWellFormedUriString(searchNameWithoutKeyword, UriKind.Absolute) && searchNameWithoutKeyword.Split('.').Last() == zip) @@ -468,7 +483,7 @@ namespace Flow.Launcher.Plugin.PluginsManager if (autocompletedResults.Any()) return autocompletedResults; - var uninstallSearch = search.Replace(Settings.HotkeyUninstall, string.Empty).TrimStart(); + var uninstallSearch = search.Replace(Settings.HotkeyUninstall, string.Empty, StringComparison.OrdinalIgnoreCase).TrimStart(); var results = Context.API .GetAllPlugins() diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/Settings.cs b/Plugins/Flow.Launcher.Plugin.PluginsManager/Settings.cs index 9c5b0d29f..a951010c0 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/Settings.cs +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/Settings.cs @@ -7,8 +7,11 @@ namespace Flow.Launcher.Plugin.PluginsManager internal class Settings { internal string HotKeyInstall { get; set; } = "install"; + internal string HotkeyUninstall { get; set; } = "uninstall"; internal string HotkeyUpdate { get; set; } = "update"; + + public bool WarnFromUnknownSource { get; set; } = true; } } diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/ViewModels/SettingsViewModel.cs b/Plugins/Flow.Launcher.Plugin.PluginsManager/ViewModels/SettingsViewModel.cs index 8ca4c614a..11303f472 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/ViewModels/SettingsViewModel.cs +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/ViewModels/SettingsViewModel.cs @@ -14,5 +14,11 @@ namespace Flow.Launcher.Plugin.PluginsManager.ViewModels Context = context; Settings = settings; } + + public bool WarnFromUnknownSource + { + get => Settings.WarnFromUnknownSource; + set => Settings.WarnFromUnknownSource = value; + } } } diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/Views/PluginsManagerSettings.xaml b/Plugins/Flow.Launcher.Plugin.PluginsManager/Views/PluginsManagerSettings.xaml index 89d27f6ff..d75803066 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/Views/PluginsManagerSettings.xaml +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/Views/PluginsManagerSettings.xaml @@ -3,10 +3,18 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" - xmlns:local="clr-namespace:Flow.Launcher.Plugin.PluginsManager.ViewModels" mc:Ignorable="d" d:DesignHeight="450" d:DesignWidth="800"> - - + + + + + + + + diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/Views/PluginsManagerSettings.xaml.cs b/Plugins/Flow.Launcher.Plugin.PluginsManager/Views/PluginsManagerSettings.xaml.cs index 703682e07..26668cc05 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/Views/PluginsManagerSettings.xaml.cs +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/Views/PluginsManagerSettings.xaml.cs @@ -16,7 +16,7 @@ namespace Flow.Launcher.Plugin.PluginsManager.Views this.viewModel = viewModel; - //RefreshView(); + this.DataContext = viewModel; } } }