mirror of
https://github.com/Flow-Launcher/Flow.Launcher.git
synced 2026-03-11 08:54:32 +00:00
Merge pull request #823 from Garulf/install-from-url
Detect URL and install using Plugin Manager
This commit is contained in:
commit
4f5eb2b454
13 changed files with 294 additions and 53 deletions
|
|
@ -1,4 +1,4 @@
|
|||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.IO;
|
||||
|
|
@ -45,8 +45,56 @@ namespace Flow.Launcher.Core.Plugin
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
return allPluginMetadata;
|
||||
|
||||
(List<PluginMetadata> uniqueList, List<PluginMetadata> duplicateList) = GetUniqueLatestPluginMetadata(allPluginMetadata);
|
||||
|
||||
duplicateList
|
||||
.ForEach(
|
||||
x => Log.Warn("PluginConfig",
|
||||
string.Format("Duplicate plugin name: {0}, id: {1}, version: {2} " +
|
||||
"not loaded due to version not the highest of the duplicates",
|
||||
x.Name, x.ID, x.Version),
|
||||
"GetUniqueLatestPluginMetadata"));
|
||||
|
||||
return uniqueList;
|
||||
}
|
||||
|
||||
internal static (List<PluginMetadata>, List<PluginMetadata>) GetUniqueLatestPluginMetadata(List<PluginMetadata> allPluginMetadata)
|
||||
{
|
||||
var duplicate_list = new List<PluginMetadata>();
|
||||
var unique_list = new List<PluginMetadata>();
|
||||
|
||||
var duplicateGroups = allPluginMetadata.GroupBy(x => x.ID).Where(g => g.Count() > 1).Select(y => y).ToList();
|
||||
|
||||
foreach (var metadata in allPluginMetadata)
|
||||
{
|
||||
var duplicatesExist = false;
|
||||
foreach (var group in duplicateGroups)
|
||||
{
|
||||
if (metadata.ID == group.Key)
|
||||
{
|
||||
duplicatesExist = true;
|
||||
|
||||
// If metadata's version greater than each duplicate's version, CompareTo > 0
|
||||
var count = group.Where(x => metadata.Version.CompareTo(x.Version) > 0).Count();
|
||||
|
||||
// Only add if the meatadata's version is the highest of all duplicates in the group
|
||||
if (count == group.Count() - 1)
|
||||
{
|
||||
unique_list.Add(metadata);
|
||||
}
|
||||
else
|
||||
{
|
||||
duplicate_list.Add(metadata);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!duplicatesExist)
|
||||
unique_list.Add(metadata);
|
||||
}
|
||||
|
||||
return (unique_list, duplicate_list);
|
||||
}
|
||||
|
||||
private static PluginMetadata GetPluginMetadata(string pluginDirectory)
|
||||
|
|
|
|||
92
Flow.Launcher.Test/PluginLoadTest.cs
Normal file
92
Flow.Launcher.Test/PluginLoadTest.cs
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
using NUnit.Framework;
|
||||
using Flow.Launcher.Core.Plugin;
|
||||
using Flow.Launcher.Plugin;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Flow.Launcher.Test
|
||||
{
|
||||
[TestFixture]
|
||||
class PluginLoadTest
|
||||
{
|
||||
[Test]
|
||||
public void GivenDuplicatePluginMetadatasWhenLoadedThenShouldReturnOnlyUniqueList()
|
||||
{
|
||||
// Given
|
||||
var duplicateList = new List<PluginMetadata>
|
||||
{
|
||||
new PluginMetadata
|
||||
{
|
||||
ID = "CEA0TYUC6D3B4085823D60DC76F28855",
|
||||
Version = "1.0.0"
|
||||
},
|
||||
new PluginMetadata
|
||||
{
|
||||
ID = "CEA0TYUC6D3B4085823D60DC76F28855",
|
||||
Version = "1.0.1"
|
||||
},
|
||||
new PluginMetadata
|
||||
{
|
||||
ID = "CEA0TYUC6D3B4085823D60DC76F28855",
|
||||
Version = "1.0.2"
|
||||
},
|
||||
new PluginMetadata
|
||||
{
|
||||
ID = "CEA0TYUC6D3B4085823D60DC76F28855",
|
||||
Version = "1.0.0"
|
||||
},
|
||||
new PluginMetadata
|
||||
{
|
||||
ID = "CEA0TYUC6D3B4085823D60DC76F28855",
|
||||
Version = "1.0.0"
|
||||
},
|
||||
new PluginMetadata
|
||||
{
|
||||
ID = "ABC0TYUC6D3B7855823D60DC76F28855",
|
||||
Version = "1.0.0"
|
||||
},
|
||||
new PluginMetadata
|
||||
{
|
||||
ID = "ABC0TYUC6D3B7855823D60DC76F28855",
|
||||
Version = "1.0.0"
|
||||
}
|
||||
};
|
||||
|
||||
// When
|
||||
(var unique, var duplicates) = PluginConfig.GetUniqueLatestPluginMetadata(duplicateList);
|
||||
|
||||
// Then
|
||||
Assert.True(unique.FirstOrDefault().ID == "CEA0TYUC6D3B4085823D60DC76F28855" && unique.FirstOrDefault().Version == "1.0.2");
|
||||
Assert.True(unique.Count() == 1);
|
||||
|
||||
Assert.False(duplicates.Any(x => x.Version == "1.0.2" && x.ID == "CEA0TYUC6D3B4085823D60DC76F28855"));
|
||||
Assert.True(duplicates.Count() == 6);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GivenDuplicatePluginMetadatasWithNoUniquePluginWhenLoadedThenShouldReturnEmptyList()
|
||||
{
|
||||
// Given
|
||||
var duplicateList = new List<PluginMetadata>
|
||||
{
|
||||
new PluginMetadata
|
||||
{
|
||||
ID = "CEA0TYUC6D3B7855823D60DC76F28855",
|
||||
Version = "1.0.0"
|
||||
},
|
||||
new PluginMetadata
|
||||
{
|
||||
ID = "CEA0TYUC6D3B7855823D60DC76F28855",
|
||||
Version = "1.0.0"
|
||||
}
|
||||
};
|
||||
|
||||
// When
|
||||
(var unique, var duplicates) = PluginConfig.GetUniqueLatestPluginMetadata(duplicateList);
|
||||
|
||||
// Then
|
||||
Assert.True(unique.Count() == 0);
|
||||
Assert.True(duplicates.Count() == 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
using NUnit.Framework;
|
||||
using Flow.Launcher.Core.Plugin;
|
||||
using Flow.Launcher.Infrastructure.Exception;
|
||||
|
||||
namespace Flow.Launcher.Test.Plugins
|
||||
{
|
||||
|
||||
[TestFixture]
|
||||
public class PluginInitTest
|
||||
{
|
||||
[Test]
|
||||
public void PublicAPIIsNullTest()
|
||||
{
|
||||
//Assert.Throws(typeof(Flow.LauncherFatalException), () => PluginManager.Initialize(null));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -4,13 +4,16 @@
|
|||
|
||||
<!--Dialogues-->
|
||||
<system:String x:Key="plugin_pluginsmanager_downloading_plugin">Downloading plugin</system:String>
|
||||
<system:String x:Key="plugin_pluginsmanager_please_wait">Please wait...</system:String>
|
||||
<system:String x:Key="plugin_pluginsmanager_download_success">Successfully downloaded</system:String>
|
||||
<system:String x:Key="plugin_pluginsmanager_download_error">Error: Unable to download the plugin</system:String>
|
||||
<system:String x:Key="plugin_pluginsmanager_uninstall_prompt">{0} by {1} {2}{3}Would you like to uninstall this plugin? After the uninstallation Flow will automatically restart.</system:String>
|
||||
<system:String x:Key="plugin_pluginsmanager_install_prompt">{0} by {1} {2}{3}Would you like to install this plugin? After the installation Flow will automatically restart.</system:String>
|
||||
<system:String x:Key="plugin_pluginsmanager_install_title">Plugin Install</system:String>
|
||||
<system:String x:Key="plugin_pluginsmanager_install_from_web">Download and install {0}</system:String>
|
||||
<system:String x:Key="plugin_pluginsmanager_uninstall_title">Plugin Uninstall</system:String>
|
||||
<system:String x:Key="plugin_pluginsmanager_install_errormetadatafile">Install failed: unable to find the plugin.json metadata file from the new plugin</system:String>
|
||||
<system:String x:Key="plugin_pluginsmanager_install_success_restart">Plugin successfully installed. Restarting Flow, please wait...</system:String>
|
||||
<system:String x:Key="plugin_pluginsmanager_install_errormetadatafile">Unable to find the plugin.json metadata file from the extracted zip file.</system:String>
|
||||
<system:String x:Key="plugin_pluginsmanager_install_error_duplicate">Error: A plugin which has the same or greater version with {0} already exists.</system:String>
|
||||
<system:String x:Key="plugin_pluginsmanager_install_error_title">Error installing plugin</system:String>
|
||||
<system:String x:Key="plugin_pluginsmanager_install_error_subtitle">Error occured while trying to install {0}</system:String>
|
||||
<system:String x:Key="plugin_pluginsmanager_update_noresult_title">No update available</system:String>
|
||||
|
|
@ -21,12 +24,15 @@
|
|||
<system:String x:Key="plugin_pluginsmanager_update_alreadyexists">This plugin is already installed</system:String>
|
||||
<system:String x:Key="plugin_pluginsmanager_update_failed_title">Plugin Manifest Download Failed</system:String>
|
||||
<system:String x:Key="plugin_pluginsmanager_update_failed_subtitle">Please check if you can connect to github.com. This error means you may not be able to install or update plugins.</system:String>
|
||||
|
||||
<system:String x:Key="plugin_pluginsmanager_install_unknown_source_warning_title">Installing from an unknown source</system:String>
|
||||
<system:String x:Key="plugin_pluginsmanager_install_unknown_source_warning">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)</system:String>
|
||||
|
||||
<!--Controls-->
|
||||
|
||||
<!--Plugin Infos-->
|
||||
<system:String x:Key="plugin_pluginsmanager_plugin_name">Plugins Manager</system:String>
|
||||
<system:String x:Key="plugin_pluginsmanager_plugin_description">Management of installing, uninstalling or updating Flow Launcher plugins</system:String>
|
||||
<system:String x:Key="plugin_pluginsmanager_unknown_author">Unknown Author</system:String>
|
||||
|
||||
<!--Context menu items-->
|
||||
<system:String x:Key="plugin_pluginsmanager_plugin_contextmenu_openwebsite_title">Open website</system:String>
|
||||
|
|
@ -36,6 +42,8 @@
|
|||
<system:String x:Key="plugin_pluginsmanager_plugin_contextmenu_newissue_title">Suggest an enhancement or submit an issue</system:String>
|
||||
<system:String x:Key="plugin_pluginsmanager_plugin_contextmenu_newissue_subtitle">Suggest an enhancement or submit an issue to the plugin developer</system:String>
|
||||
<system:String x:Key="plugin_pluginsmanager_plugin_contextmenu_pluginsmanifest_title">Go to Flow's plugins repository</system:String>
|
||||
<system:String x:Key="plugin_pluginsmanager_plugin_contextmenu_pluginsmanifest_subtitle">Visit the PluginsManifest repository to see comunity-made plugin submissions</system:String>
|
||||
|
||||
<system:String x:Key="plugin_pluginsmanager_plugin_contextmenu_pluginsmanifest_subtitle">Visit the PluginsManifest repository to see community-made plugin submissions</system:String>
|
||||
|
||||
<!--Settings menu items-->
|
||||
<system:String x:Key="plugin_pluginsmanager_plugin_settings_unknown_source">Install from unknown source warning</system:String>
|
||||
</ResourceDictionary>
|
||||
|
|
@ -4,7 +4,6 @@
|
|||
|
||||
<!--Dialogues-->
|
||||
<system:String x:Key="plugin_pluginsmanager_downloading_plugin">Sťahovanie pluginu</system:String>
|
||||
<system:String x:Key="plugin_pluginsmanager_please_wait">Čakajte, prosím…</system:String>
|
||||
<system:String x:Key="plugin_pluginsmanager_download_success">Úspešne stiahnuté</system:String>
|
||||
<system:String x:Key="plugin_pluginsmanager_uninstall_prompt">{0} od {1} {2}{3}Chcete odinštalovať tento plugin? Po odinštalovaní sa Flow automaticky reštartuje.</system:String>
|
||||
<system:String x:Key="plugin_pluginsmanager_install_prompt">{0} by {1} {2}{3}Chcete nainštalovať tento plugin? Po nainštalovaní sa Flow automaticky reštartuje.</system:String>
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@
|
|||
|
||||
<!--Dialogues-->
|
||||
<system:String x:Key="plugin_pluginsmanager_downloading_plugin">下载插件</system:String>
|
||||
<system:String x:Key="plugin_pluginsmanager_please_wait">请稍等...</system:String>
|
||||
<system:String x:Key="plugin_pluginsmanager_download_success">下载完成</system:String>
|
||||
<system:String x:Key="plugin_pluginsmanager_uninstall_prompt">{0} by {1} {2}{3} 您要卸载此插件吗? 卸载后,Flow Launcher 将自动重启。</system:String>
|
||||
<system:String x:Key="plugin_pluginsmanager_install_prompt">{0} by {1} {2}{3} 您要安装此插件吗? 安装后,Flow Launcher 将自动重启</system:String>
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ namespace Flow.Launcher.Plugin.PluginsManager
|
|||
|
||||
public async Task<List<Result>> 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;
|
||||
|
|
|
|||
|
|
@ -9,6 +9,8 @@ using System;
|
|||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Text.Json;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
|
|
@ -17,6 +19,8 @@ namespace Flow.Launcher.Plugin.PluginsManager
|
|||
{
|
||||
internal class PluginsManager
|
||||
{
|
||||
const string zip = "zip";
|
||||
|
||||
private PluginInitContext Context { get; set; }
|
||||
|
||||
private Settings Settings { get; set; }
|
||||
|
|
@ -47,7 +51,6 @@ namespace Flow.Launcher.Plugin.PluginsManager
|
|||
|
||||
private Task _downloadManifestTask = Task.CompletedTask;
|
||||
|
||||
|
||||
internal Task UpdateManifestAsync()
|
||||
{
|
||||
if (_downloadManifestTask.Status == TaskStatus.Running)
|
||||
|
|
@ -138,13 +141,15 @@ namespace Flow.Launcher.Plugin.PluginsManager
|
|||
MessageBoxButton.YesNo) == MessageBoxResult.No)
|
||||
return;
|
||||
|
||||
var filePath = Path.Combine(DataLocation.PluginsDirectory, $"{plugin.Name}-{plugin.Version}.zip");
|
||||
// at minimum should provide a name, but handle plugin that is not downloaded from plugins manifest and is a url download
|
||||
var downloadFilename = string.IsNullOrEmpty(plugin.Version)
|
||||
? $"{plugin.Name}-{Guid.NewGuid()}.zip"
|
||||
: $"{plugin.Name}-{plugin.Version}.zip";
|
||||
|
||||
var filePath = Path.Combine(DataLocation.PluginsDirectory, downloadFilename);
|
||||
|
||||
try
|
||||
{
|
||||
Context.API.ShowMsg(Context.API.GetTranslation("plugin_pluginsmanager_downloading_plugin"),
|
||||
Context.API.GetTranslation("plugin_pluginsmanager_please_wait"));
|
||||
|
||||
await Http.DownloadAsync(plugin.UrlDownload, filePath).ConfigureAwait(false);
|
||||
|
||||
Context.API.ShowMsg(Context.API.GetTranslation("plugin_pluginsmanager_downloading_plugin"),
|
||||
|
|
@ -154,7 +159,11 @@ namespace Flow.Launcher.Plugin.PluginsManager
|
|||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Context.API.ShowMsg(Context.API.GetTranslation("plugin_pluginsmanager_install_error_title"),
|
||||
if (e is HttpRequestException)
|
||||
MessageBox.Show(Context.API.GetTranslation("plugin_pluginsmanager_download_error"),
|
||||
Context.API.GetTranslation("plugin_pluginsmanager_downloading_plugin"));
|
||||
|
||||
Context.API.ShowMsgError(Context.API.GetTranslation("plugin_pluginsmanager_install_error_title"),
|
||||
string.Format(Context.API.GetTranslation("plugin_pluginsmanager_install_error_subtitle"),
|
||||
plugin.Name));
|
||||
|
||||
|
|
@ -163,6 +172,9 @@ namespace Flow.Launcher.Plugin.PluginsManager
|
|||
return;
|
||||
}
|
||||
|
||||
Context.API.ShowMsg(Context.API.GetTranslation("plugin_pluginsmanager_install_title"),
|
||||
Context.API.GetTranslation("plugin_pluginsmanager_install_success_restart"));
|
||||
|
||||
Context.API.RestartApp();
|
||||
}
|
||||
|
||||
|
|
@ -183,7 +195,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()
|
||||
|
|
@ -239,10 +251,6 @@ namespace Flow.Launcher.Plugin.PluginsManager
|
|||
|
||||
Task.Run(async delegate
|
||||
{
|
||||
Context.API.ShowMsg(
|
||||
Context.API.GetTranslation("plugin_pluginsmanager_downloading_plugin"),
|
||||
Context.API.GetTranslation("plugin_pluginsmanager_please_wait"));
|
||||
|
||||
await Http.DownloadAsync(x.PluginNewUserPlugin.UrlDownload, downloadToFilePath)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
|
|
@ -302,6 +310,62 @@ namespace Flow.Launcher.Plugin.PluginsManager
|
|||
.ToList();
|
||||
}
|
||||
|
||||
internal List<Result> InstallFromWeb(string url)
|
||||
{
|
||||
var filename = url.Split("/").Last();
|
||||
var name = filename.Split(string.Format(".{0}", zip)).First();
|
||||
|
||||
var plugin = new UserPlugin
|
||||
{
|
||||
ID = "",
|
||||
Name = name,
|
||||
Version = string.Empty,
|
||||
Author = Context.API.GetTranslation("plugin_pluginsmanager_unknown_author"),
|
||||
UrlDownload = url
|
||||
};
|
||||
|
||||
var result = new Result
|
||||
{
|
||||
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(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> { 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<List<Result>> RequestInstallOrUpdate(string searchName, CancellationToken token)
|
||||
{
|
||||
if (!PluginsManifest.UserPlugins.Any())
|
||||
|
|
@ -311,7 +375,11 @@ 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)
|
||||
return InstallFromWeb(searchNameWithoutKeyword);
|
||||
|
||||
var results =
|
||||
PluginsManifest
|
||||
|
|
@ -369,11 +437,26 @@ namespace Flow.Launcher.Plugin.PluginsManager
|
|||
|
||||
if (string.IsNullOrEmpty(metadataJsonFilePath) || string.IsNullOrEmpty(pluginFolderPath))
|
||||
{
|
||||
MessageBox.Show(Context.API.GetTranslation("plugin_pluginsmanager_install_errormetadatafile"));
|
||||
return;
|
||||
MessageBox.Show(Context.API.GetTranslation("plugin_pluginsmanager_install_errormetadatafile"),
|
||||
Context.API.GetTranslation("plugin_pluginsmanager_install_error_title"));
|
||||
|
||||
throw new FileNotFoundException (
|
||||
string.Format("Unable to find plugin.json from the extracted zip file, or this path {0} does not exist", pluginFolderPath));
|
||||
}
|
||||
|
||||
string newPluginPath = Path.Combine(DataLocation.PluginsDirectory, $"{plugin.Name}-{plugin.Version}");
|
||||
if (SameOrLesserPluginVersionExists(metadataJsonFilePath))
|
||||
{
|
||||
MessageBox.Show(string.Format(Context.API.GetTranslation("plugin_pluginsmanager_install_error_duplicate"), plugin.Name),
|
||||
Context.API.GetTranslation("plugin_pluginsmanager_install_error_title"));
|
||||
|
||||
throw new InvalidOperationException(
|
||||
string.Format("A plugin with the same ID and version already exists, " +
|
||||
"or the version is greater than this downloaded plugin {0}",
|
||||
plugin.Name));
|
||||
}
|
||||
|
||||
var directory = string.IsNullOrEmpty(plugin.Version) ? $"{plugin.Name}-{Guid.NewGuid()}" : $"{plugin.Name}-{plugin.Version}";
|
||||
var newPluginPath = Path.Combine(DataLocation.PluginsDirectory, directory);
|
||||
|
||||
FilesFolders.CopyAll(pluginFolderPath, newPluginPath);
|
||||
|
||||
|
|
@ -390,7 +473,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()
|
||||
|
|
@ -466,5 +549,13 @@ namespace Flow.Launcher.Plugin.PluginsManager
|
|||
|
||||
return new List<Result>();
|
||||
}
|
||||
|
||||
private bool SameOrLesserPluginVersionExists(string metadataPath)
|
||||
{
|
||||
var newMetadata = JsonSerializer.Deserialize<PluginMetadata>(File.ReadAllText(metadataPath));
|
||||
return Context.API.GetAllPlugins()
|
||||
.Any(x => x.Metadata.ID == newMetadata.ID
|
||||
&& newMetadata.Version.CompareTo(x.Metadata.Version) <= 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,5 +14,11 @@ namespace Flow.Launcher.Plugin.PluginsManager.ViewModels
|
|||
Context = context;
|
||||
Settings = settings;
|
||||
}
|
||||
|
||||
public bool WarnFromUnknownSource
|
||||
{
|
||||
get => Settings.WarnFromUnknownSource;
|
||||
set => Settings.WarnFromUnknownSource = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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">
|
||||
<Grid>
|
||||
|
||||
<Grid Margin="70 15 0 15">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="250"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<TextBlock Grid.Column="0"
|
||||
Text="{DynamicResource plugin_pluginsmanager_plugin_settings_unknown_source}"
|
||||
VerticalAlignment="Center"
|
||||
FontSize="14"/>
|
||||
<CheckBox Grid.Column="1" IsChecked="{Binding WarnFromUnknownSource}" />
|
||||
</Grid>
|
||||
</UserControl>
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ namespace Flow.Launcher.Plugin.PluginsManager.Views
|
|||
|
||||
this.viewModel = viewModel;
|
||||
|
||||
//RefreshView();
|
||||
this.DataContext = viewModel;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
"Name": "Plugins Manager",
|
||||
"Description": "Management of installing, uninstalling or updating Flow Launcher plugins",
|
||||
"Author": "Jeremy Wu",
|
||||
"Version": "1.9.1",
|
||||
"Version": "1.10.0",
|
||||
"Language": "csharp",
|
||||
"Website": "https://github.com/Flow-Launcher/Flow.Launcher",
|
||||
"ExecuteFileName": "Flow.Launcher.Plugin.PluginsManager.dll",
|
||||
|
|
|
|||
Loading…
Reference in a new issue