mirror of
https://github.com/Flow-Launcher/Flow.Launcher.git
synced 2026-03-11 08:54:32 +00:00
Merge pull request #231 from Flow-Launcher/add_pluginsmanager
Add PluginsManager for managing PluginsManifest repo
This commit is contained in:
commit
0eef6c2db2
35 changed files with 627 additions and 672 deletions
|
|
@ -55,7 +55,6 @@
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="FSharp.Core" Version="4.7.1" />
|
<PackageReference Include="FSharp.Core" Version="4.7.1" />
|
||||||
<PackageReference Include="squirrel.windows" Version="1.5.2" />
|
<PackageReference Include="squirrel.windows" Version="1.5.2" />
|
||||||
<PackageReference Include="SharpZipLib" Version="1.2.0" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|
|
||||||
|
|
@ -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<PluginMetadata>(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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// unzip plugin contents to the given directory.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="zipFile">The path to the zip file.</param>
|
|
||||||
/// <param name="strDirectory">The output directory.</param>
|
|
||||||
/// <param name="overWrite">overwirte</param>
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -133,11 +133,6 @@ namespace Flow.Launcher.Core.Plugin
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void InstallPlugin(string path)
|
|
||||||
{
|
|
||||||
PluginInstaller.Install(path);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static List<PluginPair> ValidPluginsForQuery(Query query)
|
public static List<PluginPair> ValidPluginsForQuery(Query query)
|
||||||
{
|
{
|
||||||
if (NonGlobalPlugins.ContainsKey(query.ActionKeyword))
|
if (NonGlobalPlugins.ContainsKey(query.ActionKeyword))
|
||||||
|
|
|
||||||
|
|
@ -66,18 +66,12 @@ namespace Flow.Launcher.Infrastructure.Http
|
||||||
response = response.NonNull();
|
response = response.NonNull();
|
||||||
var stream = response.GetResponseStream().NonNull();
|
var stream = response.GetResponseStream().NonNull();
|
||||||
|
|
||||||
using (var reader = new StreamReader(stream, Encoding.GetEncoding(encoding)))
|
using var reader = new StreamReader(stream, Encoding.GetEncoding(encoding));
|
||||||
{
|
var content = await reader.ReadToEndAsync();
|
||||||
var content = await reader.ReadToEndAsync();
|
if (response.StatusCode != HttpStatusCode.OK)
|
||||||
if (response.StatusCode == HttpStatusCode.OK)
|
throw new HttpRequestException($"Error code <{response.StatusCode}> with content <{content}> returned from <{url}>");
|
||||||
{
|
|
||||||
return content;
|
return content;
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw new HttpRequestException($"Error code <{response.StatusCode}> with content <{content}> returned from <{url}>");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -63,12 +63,6 @@ namespace Flow.Launcher.Plugin
|
||||||
/// </summary>
|
/// </summary>
|
||||||
void OpenSettingDialog();
|
void OpenSettingDialog();
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Install Flow Launcher plugin
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="path">Plugin path (ends with .flowlauncher)</param>
|
|
||||||
void InstallPlugin(string path);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Get translation of current language
|
/// Get translation of current language
|
||||||
/// You need to implement IPluginI18n if you want to support multiple languages for your plugin
|
/// You need to implement IPluginI18n if you want to support multiple languages for your plugin
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Flow.Launcher", "Flow.Launc
|
||||||
ProjectSection(ProjectDependencies) = postProject
|
ProjectSection(ProjectDependencies) = postProject
|
||||||
{1EE20B48-82FB-48A2-8086-675D6DDAB4F0} = {1EE20B48-82FB-48A2-8086-675D6DDAB4F0}
|
{1EE20B48-82FB-48A2-8086-675D6DDAB4F0} = {1EE20B48-82FB-48A2-8086-675D6DDAB4F0}
|
||||||
{0B9DE348-9361-4940-ADB6-F5953BFFCCEC} = {0B9DE348-9361-4940-ADB6-F5953BFFCCEC}
|
{0B9DE348-9361-4940-ADB6-F5953BFFCCEC} = {0B9DE348-9361-4940-ADB6-F5953BFFCCEC}
|
||||||
|
{4792A74A-0CEA-4173-A8B2-30E6764C6217} = {4792A74A-0CEA-4173-A8B2-30E6764C6217}
|
||||||
{FDB3555B-58EF-4AE6-B5F1-904719637AB4} = {FDB3555B-58EF-4AE6-B5F1-904719637AB4}
|
{FDB3555B-58EF-4AE6-B5F1-904719637AB4} = {FDB3555B-58EF-4AE6-B5F1-904719637AB4}
|
||||||
{F9C4C081-4CC3-4146-95F1-E102B4E10A5F} = {F9C4C081-4CC3-4146-95F1-E102B4E10A5F}
|
{F9C4C081-4CC3-4146-95F1-E102B4E10A5F} = {F9C4C081-4CC3-4146-95F1-E102B4E10A5F}
|
||||||
{59BD9891-3837-438A-958D-ADC7F91F6F7E} = {59BD9891-3837-438A-958D-ADC7F91F6F7E}
|
{59BD9891-3837-438A-958D-ADC7F91F6F7E} = {59BD9891-3837-438A-958D-ADC7F91F6F7E}
|
||||||
|
|
@ -23,15 +24,12 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Flow.Launcher", "Flow.Launc
|
||||||
{9B130CC5-14FB-41FF-B310-0A95B6894C37} = {9B130CC5-14FB-41FF-B310-0A95B6894C37}
|
{9B130CC5-14FB-41FF-B310-0A95B6894C37} = {9B130CC5-14FB-41FF-B310-0A95B6894C37}
|
||||||
{FDED22C8-B637-42E8-824A-63B5B6E05A3A} = {FDED22C8-B637-42E8-824A-63B5B6E05A3A}
|
{FDED22C8-B637-42E8-824A-63B5B6E05A3A} = {FDED22C8-B637-42E8-824A-63B5B6E05A3A}
|
||||||
{A3DCCBCA-ACC1-421D-B16E-210896234C26} = {A3DCCBCA-ACC1-421D-B16E-210896234C26}
|
{A3DCCBCA-ACC1-421D-B16E-210896234C26} = {A3DCCBCA-ACC1-421D-B16E-210896234C26}
|
||||||
{049490F0-ECD2-4148-9B39-2135EC346EBE} = {049490F0-ECD2-4148-9B39-2135EC346EBE}
|
|
||||||
{403B57F2-1856-4FC7-8A24-36AB346B763E} = {403B57F2-1856-4FC7-8A24-36AB346B763E}
|
{403B57F2-1856-4FC7-8A24-36AB346B763E} = {403B57F2-1856-4FC7-8A24-36AB346B763E}
|
||||||
{588088F4-3262-4F9F-9663-A05DE12534C3} = {588088F4-3262-4F9F-9663-A05DE12534C3}
|
{588088F4-3262-4F9F-9663-A05DE12534C3} = {588088F4-3262-4F9F-9663-A05DE12534C3}
|
||||||
EndProjectSection
|
EndProjectSection
|
||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Flow.Launcher.Infrastructure", "Flow.Launcher.Infrastructure\Flow.Launcher.Infrastructure.csproj", "{4FD29318-A8AB-4D8F-AA47-60BC241B8DA3}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Flow.Launcher.Infrastructure", "Flow.Launcher.Infrastructure\Flow.Launcher.Infrastructure.csproj", "{4FD29318-A8AB-4D8F-AA47-60BC241B8DA3}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Flow.Launcher.Plugin.PluginManagement", "Plugins\Flow.Launcher.Plugin.PluginManagement\Flow.Launcher.Plugin.PluginManagement.csproj", "{049490F0-ECD2-4148-9B39-2135EC346EBE}"
|
|
||||||
EndProject
|
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Flow.Launcher.Core", "Flow.Launcher.Core\Flow.Launcher.Core.csproj", "{B749F0DB-8E75-47DB-9E5E-265D16D0C0D2}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Flow.Launcher.Core", "Flow.Launcher.Core\Flow.Launcher.Core.csproj", "{B749F0DB-8E75-47DB-9E5E-265D16D0C0D2}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Flow.Launcher.Plugin.Program", "Plugins\Flow.Launcher.Plugin.Program\Flow.Launcher.Plugin.Program.csproj", "{FDB3555B-58EF-4AE6-B5F1-904719637AB4}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Flow.Launcher.Plugin.Program", "Plugins\Flow.Launcher.Plugin.Program\Flow.Launcher.Plugin.Program.csproj", "{FDB3555B-58EF-4AE6-B5F1-904719637AB4}"
|
||||||
|
|
@ -71,6 +69,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Flow.Launcher.Plugin.Explor
|
||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Flow.Launcher.Plugin.ProcessKiller", "Plugins\Flow.Launcher.Plugin.ProcessKiller\Flow.Launcher.Plugin.ProcessKiller.csproj", "{588088F4-3262-4F9F-9663-A05DE12534C3}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Flow.Launcher.Plugin.ProcessKiller", "Plugins\Flow.Launcher.Plugin.ProcessKiller\Flow.Launcher.Plugin.ProcessKiller.csproj", "{588088F4-3262-4F9F-9663-A05DE12534C3}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Flow.Launcher.Plugin.PluginsManager", "Plugins\Flow.Launcher.Plugin.PluginsManager\Flow.Launcher.Plugin.PluginsManager.csproj", "{4792A74A-0CEA-4173-A8B2-30E6764C6217}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
|
@ -129,18 +129,6 @@ Global
|
||||||
{4FD29318-A8AB-4D8F-AA47-60BC241B8DA3}.Release|x64.Build.0 = Release|Any CPU
|
{4FD29318-A8AB-4D8F-AA47-60BC241B8DA3}.Release|x64.Build.0 = Release|Any CPU
|
||||||
{4FD29318-A8AB-4D8F-AA47-60BC241B8DA3}.Release|x86.ActiveCfg = Release|Any CPU
|
{4FD29318-A8AB-4D8F-AA47-60BC241B8DA3}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
{4FD29318-A8AB-4D8F-AA47-60BC241B8DA3}.Release|x86.Build.0 = Release|Any CPU
|
{4FD29318-A8AB-4D8F-AA47-60BC241B8DA3}.Release|x86.Build.0 = Release|Any CPU
|
||||||
{049490F0-ECD2-4148-9B39-2135EC346EBE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
|
||||||
{049490F0-ECD2-4148-9B39-2135EC346EBE}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
|
||||||
{049490F0-ECD2-4148-9B39-2135EC346EBE}.Debug|x64.ActiveCfg = Debug|Any CPU
|
|
||||||
{049490F0-ECD2-4148-9B39-2135EC346EBE}.Debug|x64.Build.0 = Debug|Any CPU
|
|
||||||
{049490F0-ECD2-4148-9B39-2135EC346EBE}.Debug|x86.ActiveCfg = Debug|Any CPU
|
|
||||||
{049490F0-ECD2-4148-9B39-2135EC346EBE}.Debug|x86.Build.0 = Debug|Any CPU
|
|
||||||
{049490F0-ECD2-4148-9B39-2135EC346EBE}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
|
||||||
{049490F0-ECD2-4148-9B39-2135EC346EBE}.Release|Any CPU.Build.0 = Release|Any CPU
|
|
||||||
{049490F0-ECD2-4148-9B39-2135EC346EBE}.Release|x64.ActiveCfg = Release|Any CPU
|
|
||||||
{049490F0-ECD2-4148-9B39-2135EC346EBE}.Release|x64.Build.0 = Release|Any CPU
|
|
||||||
{049490F0-ECD2-4148-9B39-2135EC346EBE}.Release|x86.ActiveCfg = Release|Any CPU
|
|
||||||
{049490F0-ECD2-4148-9B39-2135EC346EBE}.Release|x86.Build.0 = Release|Any CPU
|
|
||||||
{B749F0DB-8E75-47DB-9E5E-265D16D0C0D2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
{B749F0DB-8E75-47DB-9E5E-265D16D0C0D2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
{B749F0DB-8E75-47DB-9E5E-265D16D0C0D2}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{B749F0DB-8E75-47DB-9E5E-265D16D0C0D2}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{B749F0DB-8E75-47DB-9E5E-265D16D0C0D2}.Debug|x64.ActiveCfg = Debug|Any CPU
|
{B749F0DB-8E75-47DB-9E5E-265D16D0C0D2}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
|
@ -298,12 +286,23 @@ Global
|
||||||
{588088F4-3262-4F9F-9663-A05DE12534C3}.Release|x64.Build.0 = Release|Any CPU
|
{588088F4-3262-4F9F-9663-A05DE12534C3}.Release|x64.Build.0 = Release|Any CPU
|
||||||
{588088F4-3262-4F9F-9663-A05DE12534C3}.Release|x86.ActiveCfg = Release|Any CPU
|
{588088F4-3262-4F9F-9663-A05DE12534C3}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
{588088F4-3262-4F9F-9663-A05DE12534C3}.Release|x86.Build.0 = Release|Any CPU
|
{588088F4-3262-4F9F-9663-A05DE12534C3}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
{4792A74A-0CEA-4173-A8B2-30E6764C6217}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{4792A74A-0CEA-4173-A8B2-30E6764C6217}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{4792A74A-0CEA-4173-A8B2-30E6764C6217}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{4792A74A-0CEA-4173-A8B2-30E6764C6217}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{4792A74A-0CEA-4173-A8B2-30E6764C6217}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{4792A74A-0CEA-4173-A8B2-30E6764C6217}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{4792A74A-0CEA-4173-A8B2-30E6764C6217}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{4792A74A-0CEA-4173-A8B2-30E6764C6217}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{4792A74A-0CEA-4173-A8B2-30E6764C6217}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{4792A74A-0CEA-4173-A8B2-30E6764C6217}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{4792A74A-0CEA-4173-A8B2-30E6764C6217}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{4792A74A-0CEA-4173-A8B2-30E6764C6217}.Release|x86.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(NestedProjects) = preSolution
|
GlobalSection(NestedProjects) = preSolution
|
||||||
{049490F0-ECD2-4148-9B39-2135EC346EBE} = {3A73F5A7-0335-40D8-BF7C-F20BE5D0BA87}
|
|
||||||
{FDB3555B-58EF-4AE6-B5F1-904719637AB4} = {3A73F5A7-0335-40D8-BF7C-F20BE5D0BA87}
|
{FDB3555B-58EF-4AE6-B5F1-904719637AB4} = {3A73F5A7-0335-40D8-BF7C-F20BE5D0BA87}
|
||||||
{403B57F2-1856-4FC7-8A24-36AB346B763E} = {3A73F5A7-0335-40D8-BF7C-F20BE5D0BA87}
|
{403B57F2-1856-4FC7-8A24-36AB346B763E} = {3A73F5A7-0335-40D8-BF7C-F20BE5D0BA87}
|
||||||
{1EE20B48-82FB-48A2-8086-675D6DDAB4F0} = {3A73F5A7-0335-40D8-BF7C-F20BE5D0BA87}
|
{1EE20B48-82FB-48A2-8086-675D6DDAB4F0} = {3A73F5A7-0335-40D8-BF7C-F20BE5D0BA87}
|
||||||
|
|
@ -316,6 +315,7 @@ Global
|
||||||
{59BD9891-3837-438A-958D-ADC7F91F6F7E} = {3A73F5A7-0335-40D8-BF7C-F20BE5D0BA87}
|
{59BD9891-3837-438A-958D-ADC7F91F6F7E} = {3A73F5A7-0335-40D8-BF7C-F20BE5D0BA87}
|
||||||
{F9C4C081-4CC3-4146-95F1-E102B4E10A5F} = {3A73F5A7-0335-40D8-BF7C-F20BE5D0BA87}
|
{F9C4C081-4CC3-4146-95F1-E102B4E10A5F} = {3A73F5A7-0335-40D8-BF7C-F20BE5D0BA87}
|
||||||
{588088F4-3262-4F9F-9663-A05DE12534C3} = {3A73F5A7-0335-40D8-BF7C-F20BE5D0BA87}
|
{588088F4-3262-4F9F-9663-A05DE12534C3} = {3A73F5A7-0335-40D8-BF7C-F20BE5D0BA87}
|
||||||
|
{4792A74A-0CEA-4173-A8B2-30E6764C6217} = {3A73F5A7-0335-40D8-BF7C-F20BE5D0BA87}
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
SolutionGuid = {F26ACB50-3F6C-4907-B0C9-1ADACC1D0DED}
|
SolutionGuid = {F26ACB50-3F6C-4907-B0C9-1ADACC1D0DED}
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,6 @@
|
||||||
Loaded="OnLoaded"
|
Loaded="OnLoaded"
|
||||||
Initialized="OnInitialized"
|
Initialized="OnInitialized"
|
||||||
Closing="OnClosing"
|
Closing="OnClosing"
|
||||||
Drop="OnDrop"
|
|
||||||
LocationChanged="OnLocationChanged"
|
LocationChanged="OnLocationChanged"
|
||||||
Deactivated="OnDeactivated"
|
Deactivated="OnDeactivated"
|
||||||
PreviewKeyDown="OnKeyDown"
|
PreviewKeyDown="OnKeyDown"
|
||||||
|
|
|
||||||
|
|
@ -199,24 +199,6 @@ namespace Flow.Launcher
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnDrop(object sender, DragEventArgs e)
|
|
||||||
{
|
|
||||||
if (e.Data.GetDataPresent(DataFormats.FileDrop))
|
|
||||||
{
|
|
||||||
// Note that you can have more than one file.
|
|
||||||
string[] files = (string[])e.Data.GetData(DataFormats.FileDrop);
|
|
||||||
if (files[0].ToLower().EndsWith(".flowlauncher"))
|
|
||||||
{
|
|
||||||
PluginManager.InstallPlugin(files[0]);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
MessageBox.Show(InternationalizationManager.Instance.GetTranslation("invalidFlowLauncherPluginFileFormat"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
e.Handled = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnPreviewDragOver(object sender, DragEventArgs e)
|
private void OnPreviewDragOver(object sender, DragEventArgs e)
|
||||||
{
|
{
|
||||||
e.Handled = true;
|
e.Handled = true;
|
||||||
|
|
|
||||||
|
|
@ -115,11 +115,6 @@ namespace Flow.Launcher
|
||||||
_mainVM.ProgressBarVisibility = Visibility.Collapsed;
|
_mainVM.ProgressBarVisibility = Visibility.Collapsed;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void InstallPlugin(string path)
|
|
||||||
{
|
|
||||||
Application.Current.Dispatcher.Invoke(() => PluginManager.InstallPlugin(path));
|
|
||||||
}
|
|
||||||
|
|
||||||
public string GetTranslation(string key)
|
public string GetTranslation(string key)
|
||||||
{
|
{
|
||||||
return InternationalizationManager.Instance.GetTranslation(key);
|
return InternationalizationManager.Instance.GetTranslation(key);
|
||||||
|
|
|
||||||
|
|
@ -1,102 +0,0 @@
|
||||||
<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">
|
|
||||||
|
|
||||||
<PropertyGroup>
|
|
||||||
<OutputType>Library</OutputType>
|
|
||||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
|
||||||
<ProjectGuid>{049490F0-ECD2-4148-9B39-2135EC346EBE}</ProjectGuid>
|
|
||||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
|
||||||
<RootNamespace>Flow.Launcher.Plugin.PluginManagement</RootNamespace>
|
|
||||||
<AssemblyName>Flow.Launcher.Plugin.PluginManagement</AssemblyName>
|
|
||||||
<UseWindowsForms>true</UseWindowsForms>
|
|
||||||
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
|
||||||
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
|
||||||
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
|
||||||
<DebugSymbols>true</DebugSymbols>
|
|
||||||
<DebugType>full</DebugType>
|
|
||||||
<Optimize>false</Optimize>
|
|
||||||
<OutputPath>..\..\Output\Debug\Plugins\Flow.Launcher.Plugin.PluginManagement\</OutputPath>
|
|
||||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
|
||||||
<ErrorReport>prompt</ErrorReport>
|
|
||||||
<WarningLevel>4</WarningLevel>
|
|
||||||
<Prefer32Bit>false</Prefer32Bit>
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
|
||||||
<DebugType>pdbonly</DebugType>
|
|
||||||
<Optimize>true</Optimize>
|
|
||||||
<OutputPath>..\..\Output\Release\Plugins\Flow.Launcher.Plugin.PluginManagement\</OutputPath>
|
|
||||||
<DefineConstants>TRACE</DefineConstants>
|
|
||||||
<ErrorReport>prompt</ErrorReport>
|
|
||||||
<WarningLevel>4</WarningLevel>
|
|
||||||
<Prefer32Bit>false</Prefer32Bit>
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<ProjectReference Include="..\..\Flow.Launcher.Infrastructure\Flow.Launcher.Infrastructure.csproj" />
|
|
||||||
<ProjectReference Include="..\..\Flow.Launcher.Plugin\Flow.Launcher.Plugin.csproj" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<None Include="plugin.json">
|
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
|
||||||
</None>
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<None Include="Images\plugin.png">
|
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
|
||||||
</None>
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<Content Include="Languages\en.xaml">
|
|
||||||
<Generator>MSBuild:Compile</Generator>
|
|
||||||
<SubType>Designer</SubType>
|
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
|
||||||
</Content>
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<Content Include="Languages\zh-cn.xaml">
|
|
||||||
<Generator>MSBuild:Compile</Generator>
|
|
||||||
<SubType>Designer</SubType>
|
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
|
||||||
</Content>
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<Content Include="Languages\zh-tw.xaml">
|
|
||||||
<Generator>MSBuild:Compile</Generator>
|
|
||||||
<SubType>Designer</SubType>
|
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
|
||||||
</Content>
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<Content Include="Languages\de.xaml">
|
|
||||||
<Generator>MSBuild:Compile</Generator>
|
|
||||||
<SubType>Designer</SubType>
|
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
|
||||||
</Content>
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<Content Include="Languages\pl.xaml">
|
|
||||||
<Generator>MSBuild:Compile</Generator>
|
|
||||||
<SubType>Designer</SubType>
|
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
|
||||||
</Content>
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<Content Include="Languages\tr.xaml">
|
|
||||||
<Generator>MSBuild:Compile</Generator>
|
|
||||||
<SubType>Designer</SubType>
|
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
|
||||||
</Content>
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
</Project>
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
namespace Flow.Launcher.Plugin.PluginManagement
|
|
||||||
{
|
|
||||||
public class FlowLauncherPluginResult
|
|
||||||
{
|
|
||||||
public string plugin_file;
|
|
||||||
public string description;
|
|
||||||
public int liked_count;
|
|
||||||
public string name;
|
|
||||||
public string version;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 269 B |
|
|
@ -1,8 +0,0 @@
|
||||||
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
|
||||||
xmlns:system="clr-namespace:System;assembly=mscorlib">
|
|
||||||
|
|
||||||
<system:String x:Key="flowlauncher_plugin_plugin_management_plugin_name">Flow Launcher Plugin Verwaltung</system:String>
|
|
||||||
<system:String x:Key="flowlauncher_plugin_plugin_management_plugin_description">Installiere/Entferne/Aktualisiere Flow Launcher Plugins</system:String>
|
|
||||||
|
|
||||||
</ResourceDictionary>
|
|
||||||
|
|
@ -1,8 +0,0 @@
|
||||||
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
|
||||||
xmlns:system="clr-namespace:System;assembly=mscorlib">
|
|
||||||
|
|
||||||
<system:String x:Key="flowlauncher_plugin_plugin_management_plugin_name">Plugin Management</system:String>
|
|
||||||
<system:String x:Key="flowlauncher_plugin_plugin_management_plugin_description">Install, remove or update Flow Launcher plugins</system:String>
|
|
||||||
|
|
||||||
</ResourceDictionary>
|
|
||||||
|
|
@ -1,8 +0,0 @@
|
||||||
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
|
||||||
xmlns:system="clr-namespace:System;assembly=mscorlib">
|
|
||||||
|
|
||||||
<system:String x:Key="flowlauncher_plugin_plugin_management_plugin_name">Zarządzanie wtyczkami Flow Launcher</system:String>
|
|
||||||
<system:String x:Key="flowlauncher_plugin_plugin_management_plugin_description">Pozwala na instalacje, usuwanie i aktualizacje wtyczek</system:String>
|
|
||||||
|
|
||||||
</ResourceDictionary>
|
|
||||||
|
|
@ -1,8 +0,0 @@
|
||||||
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
|
||||||
xmlns:system="clr-namespace:System;assembly=mscorlib">
|
|
||||||
|
|
||||||
<system:String x:Key="flowlauncher_plugin_plugin_management_plugin_name">Správca pluginov</system:String>
|
|
||||||
<system:String x:Key="flowlauncher_plugin_plugin_management_plugin_description">Inštalácia, odinštalácia alebo aktualizácia pluginov Flow Launchera</system:String>
|
|
||||||
|
|
||||||
</ResourceDictionary>
|
|
||||||
|
|
@ -1,8 +0,0 @@
|
||||||
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
|
||||||
xmlns:system="clr-namespace:System;assembly=mscorlib">
|
|
||||||
|
|
||||||
<system:String x:Key="flowlauncher_plugin_plugin_management_plugin_name">Flow Launcher Eklenti Yöneticisi</system:String>
|
|
||||||
<system:String x:Key="flowlauncher_plugin_plugin_management_plugin_description">Flow Launcher eklentilerini kurun, kaldırın ya da güncelleyin</system:String>
|
|
||||||
|
|
||||||
</ResourceDictionary>
|
|
||||||
|
|
@ -1,8 +0,0 @@
|
||||||
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
|
||||||
xmlns:system="clr-namespace:System;assembly=mscorlib">
|
|
||||||
|
|
||||||
<system:String x:Key="flowlauncher_plugin_plugin_management_plugin_name">Flow Launcher插件管理</system:String>
|
|
||||||
<system:String x:Key="flowlauncher_plugin_plugin_management_plugin_description">安装/卸载/更新Flow Launcher插件</system:String>
|
|
||||||
|
|
||||||
</ResourceDictionary>
|
|
||||||
|
|
@ -1,8 +0,0 @@
|
||||||
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
|
||||||
xmlns:system="clr-namespace:System;assembly=mscorlib">
|
|
||||||
|
|
||||||
<system:String x:Key="flowlauncher_plugin_plugin_management_plugin_name">Flow Launcher 外掛管理</system:String>
|
|
||||||
<system:String x:Key="flowlauncher_plugin_plugin_management_plugin_description">安裝/解除安裝/更新 Flow Launcher 外掛</system:String>
|
|
||||||
|
|
||||||
</ResourceDictionary>
|
|
||||||
|
|
@ -1,258 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Net;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using System.Windows;
|
|
||||||
using Newtonsoft.Json;
|
|
||||||
using Flow.Launcher.Infrastructure;
|
|
||||||
using Flow.Launcher.Infrastructure.Http;
|
|
||||||
using Flow.Launcher.Infrastructure.Logger;
|
|
||||||
|
|
||||||
namespace Flow.Launcher.Plugin.PluginManagement
|
|
||||||
{
|
|
||||||
public class Main : IPlugin, IPluginI18n
|
|
||||||
{
|
|
||||||
private static string APIBASE = "http://api.wox.one";
|
|
||||||
private static string pluginSearchUrl = APIBASE + "/plugin/search/";
|
|
||||||
private const string ListCommand = "list";
|
|
||||||
private const string InstallCommand = "install";
|
|
||||||
private const string UninstallCommand = "uninstall";
|
|
||||||
private PluginInitContext context;
|
|
||||||
|
|
||||||
public List<Result> Query(Query query)
|
|
||||||
{
|
|
||||||
List<Result> results = new List<Result>();
|
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(query.Search))
|
|
||||||
{
|
|
||||||
results.Add(ResultForListCommandAutoComplete(query));
|
|
||||||
results.Add(ResultForInstallCommandAutoComplete(query));
|
|
||||||
results.Add(ResultForUninstallCommandAutoComplete(query));
|
|
||||||
return results;
|
|
||||||
}
|
|
||||||
|
|
||||||
string command = query.FirstSearch.ToLower();
|
|
||||||
if (string.IsNullOrEmpty(command)) return results;
|
|
||||||
|
|
||||||
if (command == ListCommand)
|
|
||||||
{
|
|
||||||
return ResultForListInstalledPlugins();
|
|
||||||
}
|
|
||||||
if (command == UninstallCommand)
|
|
||||||
{
|
|
||||||
return ResultForUnInstallPlugin(query);
|
|
||||||
}
|
|
||||||
if (command == InstallCommand)
|
|
||||||
{
|
|
||||||
return ResultForInstallPlugin(query);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (InstallCommand.Contains(command))
|
|
||||||
{
|
|
||||||
results.Add(ResultForInstallCommandAutoComplete(query));
|
|
||||||
}
|
|
||||||
if (UninstallCommand.Contains(command))
|
|
||||||
{
|
|
||||||
results.Add(ResultForUninstallCommandAutoComplete(query));
|
|
||||||
}
|
|
||||||
if (ListCommand.Contains(command))
|
|
||||||
{
|
|
||||||
results.Add(ResultForListCommandAutoComplete(query));
|
|
||||||
}
|
|
||||||
|
|
||||||
return results;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Result ResultForListCommandAutoComplete(Query query)
|
|
||||||
{
|
|
||||||
string title = ListCommand;
|
|
||||||
string subtitle = "list installed plugins";
|
|
||||||
return ResultForCommand(query, ListCommand, title, subtitle);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Result ResultForInstallCommandAutoComplete(Query query)
|
|
||||||
{
|
|
||||||
string title = $"{InstallCommand} <Package Name>";
|
|
||||||
string subtitle = "list installed plugins";
|
|
||||||
return ResultForCommand(query, InstallCommand, title, subtitle);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Result ResultForUninstallCommandAutoComplete(Query query)
|
|
||||||
{
|
|
||||||
string title = $"{UninstallCommand} <Package Name>";
|
|
||||||
string subtitle = "list installed plugins";
|
|
||||||
return ResultForCommand(query, UninstallCommand, title, subtitle);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Result ResultForCommand(Query query, string command, string title, string subtitle)
|
|
||||||
{
|
|
||||||
const string seperater = Plugin.Query.TermSeperater;
|
|
||||||
var result = new Result
|
|
||||||
{
|
|
||||||
Title = title,
|
|
||||||
IcoPath = "Images\\plugin.png",
|
|
||||||
SubTitle = subtitle,
|
|
||||||
Action = e =>
|
|
||||||
{
|
|
||||||
context.API.ChangeQuery($"{query.ActionKeyword}{seperater}{command}{seperater}");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<Result> ResultForInstallPlugin(Query query)
|
|
||||||
{
|
|
||||||
List<Result> results = new List<Result>();
|
|
||||||
string pluginName = query.SecondSearch;
|
|
||||||
if (string.IsNullOrEmpty(pluginName)) return results;
|
|
||||||
string json;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
json = Http.Get(pluginSearchUrl + pluginName).Result;
|
|
||||||
}
|
|
||||||
catch (WebException e)
|
|
||||||
{
|
|
||||||
//todo happlebao add option in log to decide give user prompt or not
|
|
||||||
context.API.ShowMsg("PluginManagement.ResultForInstallPlugin: Can't connect to Wox plugin website, check your conenction");
|
|
||||||
Log.Exception("|PluginManagement.ResultForInstallPlugin|Can't connect to Wox plugin website, check your conenction", e);
|
|
||||||
return new List<Result>();
|
|
||||||
}
|
|
||||||
List<FlowLauncherPluginResult> searchedPlugins;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
searchedPlugins = JsonConvert.DeserializeObject<List<FlowLauncherPluginResult>>(json);
|
|
||||||
}
|
|
||||||
catch (JsonSerializationException e)
|
|
||||||
{
|
|
||||||
context.API.ShowMsg("PluginManagement.ResultForInstallPlugin: Coundn't parse api search results, Please update your Flow Launcher!");
|
|
||||||
Log.Exception("|PluginManagement.ResultForInstallPlugin|Coundn't parse api search results, Please update your Flow Launcher!", e);
|
|
||||||
return results;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (FlowLauncherPluginResult r in searchedPlugins)
|
|
||||||
{
|
|
||||||
FlowLauncherPluginResult r1 = r;
|
|
||||||
results.Add(new Result
|
|
||||||
{
|
|
||||||
Title = r.name,
|
|
||||||
SubTitle = r.description,
|
|
||||||
IcoPath = "Images\\plugin.png",
|
|
||||||
TitleHighlightData = StringMatcher.FuzzySearch(query.SecondSearch, r.name).MatchData,
|
|
||||||
SubTitleHighlightData = StringMatcher.FuzzySearch(query.SecondSearch, r.description).MatchData,
|
|
||||||
Action = c =>
|
|
||||||
{
|
|
||||||
MessageBoxResult result = MessageBox.Show("Are you sure you wish to install the \'" + r.name + "\' plugin",
|
|
||||||
"Install plugin", MessageBoxButton.YesNo);
|
|
||||||
|
|
||||||
if (result == MessageBoxResult.Yes)
|
|
||||||
{
|
|
||||||
string folder = Path.Combine(Path.GetTempPath(), "FlowLauncherPluginDownload");
|
|
||||||
if (!Directory.Exists(folder)) Directory.CreateDirectory(folder);
|
|
||||||
string filePath = Path.Combine(folder, Guid.NewGuid().ToString() + ".flowlauncher");
|
|
||||||
|
|
||||||
string pluginUrl = APIBASE + "/media/" + r1.plugin_file;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
Http.Download(pluginUrl, filePath);
|
|
||||||
}
|
|
||||||
catch (WebException e)
|
|
||||||
{
|
|
||||||
context.API.ShowMsg($"PluginManagement.ResultForInstallPlugin: download failed for <{r.name}>");
|
|
||||||
Log.Exception($"|PluginManagement.ResultForInstallPlugin|download failed for <{r.name}>", e);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
context.API.InstallPlugin(filePath);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return results;
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<Result> ResultForUnInstallPlugin(Query query)
|
|
||||||
{
|
|
||||||
List<Result> results = new List<Result>();
|
|
||||||
List<PluginMetadata> allInstalledPlugins = context.API.GetAllPlugins().Select(o => o.Metadata).ToList();
|
|
||||||
if (!string.IsNullOrEmpty(query.SecondSearch))
|
|
||||||
{
|
|
||||||
allInstalledPlugins =
|
|
||||||
allInstalledPlugins.Where(o => o.Name.ToLower().Contains(query.SecondSearch.ToLower())).ToList();
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (PluginMetadata plugin in allInstalledPlugins)
|
|
||||||
{
|
|
||||||
results.Add(new Result
|
|
||||||
{
|
|
||||||
Title = plugin.Name,
|
|
||||||
SubTitle = plugin.Description,
|
|
||||||
IcoPath = plugin.IcoPath,
|
|
||||||
TitleHighlightData = StringMatcher.FuzzySearch(query.SecondSearch, plugin.Name).MatchData,
|
|
||||||
SubTitleHighlightData = StringMatcher.FuzzySearch(query.SecondSearch, plugin.Description).MatchData,
|
|
||||||
Action = e =>
|
|
||||||
{
|
|
||||||
UnInstallPlugin(plugin);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return results;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void UnInstallPlugin(PluginMetadata plugin)
|
|
||||||
{
|
|
||||||
string content = $"Do you want to uninstall following plugin?{Environment.NewLine}{Environment.NewLine}" +
|
|
||||||
$"Name: {plugin.Name}{Environment.NewLine}" +
|
|
||||||
$"Version: {plugin.Version}{Environment.NewLine}" +
|
|
||||||
$"Author: {plugin.Author}";
|
|
||||||
if (MessageBox.Show(content, "Flow Launcher", MessageBoxButton.YesNo) == MessageBoxResult.Yes)
|
|
||||||
{
|
|
||||||
File.Create(Path.Combine(plugin.PluginDirectory, "NeedDelete.txt")).Close();
|
|
||||||
var result = MessageBox.Show($"You have uninstalled plugin {plugin.Name} successfully.{Environment.NewLine}" +
|
|
||||||
"Restart Flow Launcher to take effect?",
|
|
||||||
"Install plugin", MessageBoxButton.YesNo, MessageBoxImage.Question);
|
|
||||||
if (result == MessageBoxResult.Yes)
|
|
||||||
{
|
|
||||||
context.API.RestartApp();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<Result> ResultForListInstalledPlugins()
|
|
||||||
{
|
|
||||||
List<Result> results = new List<Result>();
|
|
||||||
foreach (PluginMetadata plugin in context.API.GetAllPlugins().Select(o => o.Metadata))
|
|
||||||
{
|
|
||||||
string actionKeywordString = string.Join(" or ", plugin.ActionKeywords.ToArray());
|
|
||||||
results.Add(new Result
|
|
||||||
{
|
|
||||||
Title = $"{plugin.Name} - Action Keywords: {actionKeywordString}",
|
|
||||||
SubTitle = plugin.Description,
|
|
||||||
IcoPath = plugin.IcoPath
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return results;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Init(PluginInitContext context)
|
|
||||||
{
|
|
||||||
this.context = context;
|
|
||||||
}
|
|
||||||
|
|
||||||
public string GetTranslatedPluginTitle()
|
|
||||||
{
|
|
||||||
return context.API.GetTranslation("flowlauncher_plugin_plugin_management_plugin_name");
|
|
||||||
}
|
|
||||||
|
|
||||||
public string GetTranslatedPluginDescription()
|
|
||||||
{
|
|
||||||
return context.API.GetTranslation("flowlauncher_plugin_plugin_management_plugin_description");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,12 +0,0 @@
|
||||||
{
|
|
||||||
"ID": "D2D2C23B084D422DB66FE0C79D6C2A6A",
|
|
||||||
"ActionKeyword": "wpm",
|
|
||||||
"Name": "Plugin Management",
|
|
||||||
"Description": "Install/Remove/Update Flow Launcher plugins",
|
|
||||||
"Author": "qianlifeng",
|
|
||||||
"Version": "1.1.1",
|
|
||||||
"Language": "csharp",
|
|
||||||
"Website": "https://github.com/Flow-Launcher/Flow.Launcher",
|
|
||||||
"ExecuteFileName": "Flow.Launcher.Plugin.PluginManagement.dll",
|
|
||||||
"IcoPath": "Images\\plugin.png"
|
|
||||||
}
|
|
||||||
29
Plugins/Flow.Launcher.Plugin.PluginsManager/ContextMenu.cs
Normal file
29
Plugins/Flow.Launcher.Plugin.PluginsManager/ContextMenu.cs
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
using Flow.Launcher.Infrastructure.UserSettings;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace Flow.Launcher.Plugin.PluginsManager
|
||||||
|
{
|
||||||
|
internal class ContextMenu : IContextMenu
|
||||||
|
{
|
||||||
|
private PluginInitContext Context { get; set; }
|
||||||
|
|
||||||
|
private Settings Settings { get; set; }
|
||||||
|
|
||||||
|
public ContextMenu(PluginInitContext context, Settings settings)
|
||||||
|
{
|
||||||
|
Context = context;
|
||||||
|
Settings = settings;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Result> LoadContextMenus(Result selectedResult)
|
||||||
|
{
|
||||||
|
// Open website
|
||||||
|
// Go to source code
|
||||||
|
// Report an issue?
|
||||||
|
// Request a feature?
|
||||||
|
return new List<Result>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,46 @@
|
||||||
|
<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutputType>Library</OutputType>
|
||||||
|
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||||
|
<UseWPF>true</UseWPF>
|
||||||
|
<UseWindowsForms>true</UseWindowsForms>
|
||||||
|
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
||||||
|
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||||
|
<OutputPath>..\..\Output\Debug\Plugins\Flow.Launcher.Plugin.PluginsManager</OutputPath>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
||||||
|
<OutputPath>..\..\Output\Release\Plugins\Flow.Launcher.Plugin.PluginsManager</OutputPath>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\..\Flow.Launcher.Infrastructure\Flow.Launcher.Infrastructure.csproj" />
|
||||||
|
<ProjectReference Include="..\..\Flow.Launcher.Plugin\Flow.Launcher.Plugin.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<None Include="plugin.json">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</None>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Content Include="Images\**">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Content Include="Languages\**">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="SharpZipLib" Version="1.2.0" />
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 484 KiB |
|
|
@ -0,0 +1,24 @@
|
||||||
|
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:system="clr-namespace:System;assembly=mscorlib">
|
||||||
|
|
||||||
|
<!--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_uninstall_prompt">Do you want to uninstall the following plugin?{0}{1}{2} by {3}</system:String>
|
||||||
|
<system:String x:Key="plugin_pluginsmanager_install_prompt">Do you want to install the following plugin?{0}{1}{2} by {3}</system:String>
|
||||||
|
<system:String x:Key="plugin_pluginsmanager_install_title">Plugin Install</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_successandrestart">You have installed plugin {0} successfully.{1}Would you like to restart Flow Launcher to take effect?</system:String>
|
||||||
|
<system:String x:Key="plugin_pluginsmanager_uninstall_successandrestart">You have uninstalled plugin {0} successfully.{1}Would you like to restart Flow Launcher to take effect?</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>
|
||||||
|
|
||||||
|
<!--Context menu items-->
|
||||||
|
|
||||||
|
</ResourceDictionary>
|
||||||
66
Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs
Normal file
66
Plugins/Flow.Launcher.Plugin.PluginsManager/Main.cs
Normal file
|
|
@ -0,0 +1,66 @@
|
||||||
|
using Flow.Launcher.Infrastructure.Storage;
|
||||||
|
using Flow.Launcher.Infrastructure.UserSettings;
|
||||||
|
using Flow.Launcher.Plugin.PluginsManager.ViewModels;
|
||||||
|
using Flow.Launcher.Plugin.PluginsManager.Views;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Windows.Controls;
|
||||||
|
|
||||||
|
namespace Flow.Launcher.Plugin.PluginsManager
|
||||||
|
{
|
||||||
|
public class Main : ISettingProvider, IPlugin, ISavable, IContextMenu, IPluginI18n
|
||||||
|
{
|
||||||
|
internal PluginInitContext Context { get; set; }
|
||||||
|
|
||||||
|
internal Settings Settings;
|
||||||
|
|
||||||
|
private SettingsViewModel viewModel;
|
||||||
|
|
||||||
|
private IContextMenu contextMenu;
|
||||||
|
|
||||||
|
public Control CreateSettingPanel()
|
||||||
|
{
|
||||||
|
return new PluginsManagerSettings(viewModel);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Init(PluginInitContext context)
|
||||||
|
{
|
||||||
|
Context = context;
|
||||||
|
viewModel = new SettingsViewModel(context);
|
||||||
|
Settings = viewModel.Settings;
|
||||||
|
contextMenu = new ContextMenu(Context, Settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Result> LoadContextMenus(Result selectedResult)
|
||||||
|
{
|
||||||
|
return contextMenu.LoadContextMenus(selectedResult);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Result> Query(Query query)
|
||||||
|
{
|
||||||
|
var search = query.Search.ToLower();
|
||||||
|
|
||||||
|
var pluginManager = new PluginsManager(Context, Settings);
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(search)
|
||||||
|
&& ($"{Settings.UninstallHotkey} ".StartsWith(search) || search.StartsWith($"{Settings.UninstallHotkey} ")))
|
||||||
|
return pluginManager.RequestUninstall(search);
|
||||||
|
|
||||||
|
return pluginManager.RequestInstallOrUpdate(search);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Save()
|
||||||
|
{
|
||||||
|
viewModel.Save();
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GetTranslatedPluginTitle()
|
||||||
|
{
|
||||||
|
return Context.API.GetTranslation("plugin_pluginsmanager_plugin_name");
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GetTranslatedPluginDescription()
|
||||||
|
{
|
||||||
|
return Context.API.GetTranslation("plugin_pluginsmanager_plugin_description");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,40 @@
|
||||||
|
using Flow.Launcher.Infrastructure.Http;
|
||||||
|
using Flow.Launcher.Infrastructure.Logger;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Flow.Launcher.Plugin.PluginsManager.Models
|
||||||
|
{
|
||||||
|
internal class PluginsManifest
|
||||||
|
{
|
||||||
|
internal List<UserPlugin> UserPlugins { get; private set; }
|
||||||
|
internal PluginsManifest()
|
||||||
|
{
|
||||||
|
DownloadManifest();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DownloadManifest()
|
||||||
|
{
|
||||||
|
var json = string.Empty;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var t = Task.Run(
|
||||||
|
async () =>
|
||||||
|
json = await Http.Get("https://raw.githubusercontent.com/Flow-Launcher/Flow.Launcher.PluginsManifest/main/plugins.json"));
|
||||||
|
|
||||||
|
t.Wait();
|
||||||
|
|
||||||
|
UserPlugins = JsonConvert.DeserializeObject<List<UserPlugin>>(json);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Log.Exception("|PluginManagement.GetManifest|Encountered error trying to download plugins manifest", e);
|
||||||
|
|
||||||
|
UserPlugins = new List<UserPlugin>();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace Flow.Launcher.Plugin.PluginsManager.Models
|
||||||
|
{
|
||||||
|
public class UserPlugin
|
||||||
|
{
|
||||||
|
public string ID { get; set; }
|
||||||
|
public string Name { get; set; }
|
||||||
|
public string Description { get; set; }
|
||||||
|
public string Author { get; set; }
|
||||||
|
public string Version { get; set; }
|
||||||
|
public string Language { get; set; }
|
||||||
|
public string Website { get; set; }
|
||||||
|
public string UrlDownload { get; set; }
|
||||||
|
public string UrlSourceCode { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
235
Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs
Normal file
235
Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs
Normal file
|
|
@ -0,0 +1,235 @@
|
||||||
|
using Flow.Launcher.Infrastructure;
|
||||||
|
using Flow.Launcher.Infrastructure.Http;
|
||||||
|
using Flow.Launcher.Infrastructure.Logger;
|
||||||
|
using Flow.Launcher.Infrastructure.UserSettings;
|
||||||
|
using Flow.Launcher.Plugin.PluginsManager.Models;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Windows;
|
||||||
|
|
||||||
|
namespace Flow.Launcher.Plugin.PluginsManager
|
||||||
|
{
|
||||||
|
internal class PluginsManager
|
||||||
|
{
|
||||||
|
private readonly PluginsManifest pluginsManifest;
|
||||||
|
|
||||||
|
private PluginInitContext Context { get; set; }
|
||||||
|
|
||||||
|
private Settings Settings { get; set; }
|
||||||
|
|
||||||
|
private readonly string icoPath = "Images\\pluginsmanager.png";
|
||||||
|
|
||||||
|
internal PluginsManager(PluginInitContext context, Settings settings)
|
||||||
|
{
|
||||||
|
pluginsManifest = new PluginsManifest();
|
||||||
|
Context = context;
|
||||||
|
Settings = settings;
|
||||||
|
}
|
||||||
|
internal void InstallOrUpdate(UserPlugin plugin)
|
||||||
|
{
|
||||||
|
if (PluginExists(plugin.ID))
|
||||||
|
{
|
||||||
|
Context.API.ShowMsg("Plugin already installed");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var message = string.Format(Context.API.GetTranslation("plugin_pluginsmanager_install_prompt"),
|
||||||
|
Environment.NewLine, Environment.NewLine,
|
||||||
|
plugin.Name, plugin.Author);
|
||||||
|
|
||||||
|
if (MessageBox.Show(message, Context.API.GetTranslation("plugin_pluginsmanager_install_title"), MessageBoxButton.YesNo) == MessageBoxResult.No)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var filePath = Path.Combine(DataLocation.PluginsDirectory, $"{plugin.Name}{plugin.ID}.zip");
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Context.API.ShowMsg(Context.API.GetTranslation("plugin_pluginsmanager_downloading_plugin"),
|
||||||
|
Context.API.GetTranslation("plugin_pluginsmanager_please_wait"));
|
||||||
|
|
||||||
|
Http.Download(plugin.UrlDownload, filePath);
|
||||||
|
|
||||||
|
Context.API.ShowMsg(Context.API.GetTranslation("plugin_pluginsmanager_downloading_plugin"),
|
||||||
|
Context.API.GetTranslation("plugin_pluginsmanager_download_success"));
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Context.API.ShowMsg(Context.API.GetTranslation("plugin_pluginsmanager_downloading_plugin"),
|
||||||
|
Context.API.GetTranslation("plugin_pluginsmanager_download_success"));
|
||||||
|
|
||||||
|
Log.Exception("PluginsManager", "An error occured while downloading plugin", e, "PluginDownload");
|
||||||
|
}
|
||||||
|
|
||||||
|
Application.Current.Dispatcher.Invoke(() => Install(plugin, filePath));
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void Update()
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
internal bool PluginExists(string id)
|
||||||
|
{
|
||||||
|
return Context.API.GetAllPlugins().Any(x => x.Metadata.ID == id);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void PluginsManifestSiteOpen()
|
||||||
|
{
|
||||||
|
//Open from context menu https://git.vcmq.workers.dev/Flow-Launcher/Flow.Launcher.PluginsManifest
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
internal List<Result> Search(List<Result> results, string searchName)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(searchName))
|
||||||
|
return results;
|
||||||
|
|
||||||
|
return results
|
||||||
|
.Where(x =>
|
||||||
|
{
|
||||||
|
var matchResult = StringMatcher.FuzzySearch(searchName, x.Title);
|
||||||
|
if (matchResult.IsSearchPrecisionScoreMet())
|
||||||
|
x.Score = matchResult.Score;
|
||||||
|
|
||||||
|
return matchResult.IsSearchPrecisionScoreMet();
|
||||||
|
})
|
||||||
|
.ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
internal List<Result> RequestInstallOrUpdate(string searchName)
|
||||||
|
{
|
||||||
|
var results =
|
||||||
|
pluginsManifest
|
||||||
|
.UserPlugins
|
||||||
|
.Select(x =>
|
||||||
|
new Result
|
||||||
|
{
|
||||||
|
Title = $"{x.Name} by {x.Author}",
|
||||||
|
SubTitle = x.Description,
|
||||||
|
IcoPath = icoPath,
|
||||||
|
Action = e =>
|
||||||
|
{
|
||||||
|
Application.Current.MainWindow.Hide();
|
||||||
|
InstallOrUpdate(x);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
return Search(results, searchName);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Install(UserPlugin plugin, string downloadedFilePath)
|
||||||
|
{
|
||||||
|
if (!File.Exists(downloadedFilePath))
|
||||||
|
return;
|
||||||
|
|
||||||
|
var tempFolderPath = Path.Combine(Path.GetTempPath(), "flowlauncher");
|
||||||
|
var tempFolderPluginPath = Path.Combine(tempFolderPath, "plugin");
|
||||||
|
|
||||||
|
if (Directory.Exists(tempFolderPath))
|
||||||
|
Directory.Delete(tempFolderPath, true);
|
||||||
|
|
||||||
|
Directory.CreateDirectory(tempFolderPath);
|
||||||
|
|
||||||
|
var zipFilePath = Path.Combine(tempFolderPath, Path.GetFileName(downloadedFilePath));
|
||||||
|
|
||||||
|
File.Move(downloadedFilePath, zipFilePath);
|
||||||
|
|
||||||
|
Utilities.UnZip(zipFilePath, tempFolderPluginPath, true);
|
||||||
|
|
||||||
|
var pluginFolderPath = Utilities.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))
|
||||||
|
{
|
||||||
|
MessageBox.Show(Context.API.GetTranslation("plugin_pluginsmanager_install_errormetadatafile"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
string newPluginPath = Path.Combine(DataLocation.PluginsDirectory, $"{plugin.Name}{plugin.ID}");
|
||||||
|
|
||||||
|
Directory.Move(pluginFolderPath, newPluginPath);
|
||||||
|
|
||||||
|
if (MessageBox.Show(string.Format(Context.API.GetTranslation("plugin_pluginsmanager_install_successandrestart"),
|
||||||
|
plugin.Name, Environment.NewLine),
|
||||||
|
Context.API.GetTranslation("plugin_pluginsmanager_install_title"),
|
||||||
|
MessageBoxButton.YesNo, MessageBoxImage.Question) == MessageBoxResult.Yes)
|
||||||
|
Context.API.RestartApp();
|
||||||
|
}
|
||||||
|
|
||||||
|
internal List<Result> RequestUninstall(string search)
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrEmpty(search)
|
||||||
|
&& Settings.UninstallHotkey.StartsWith(search)
|
||||||
|
&& (Settings.UninstallHotkey != search || !search.StartsWith(Settings.UninstallHotkey)))
|
||||||
|
{
|
||||||
|
return
|
||||||
|
new List<Result>
|
||||||
|
{
|
||||||
|
new Result
|
||||||
|
{
|
||||||
|
Title = "Uninstall",
|
||||||
|
IcoPath = icoPath,
|
||||||
|
SubTitle = "Select a plugin to uninstall",
|
||||||
|
Action = e =>
|
||||||
|
{
|
||||||
|
Context
|
||||||
|
.API
|
||||||
|
.ChangeQuery($"{Context.CurrentPluginMetadata.ActionKeywords.FirstOrDefault()} {Settings.UninstallHotkey} ");
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
var uninstallSearch = search.Replace(Settings.UninstallHotkey, string.Empty).TrimStart();
|
||||||
|
|
||||||
|
var results= Context.API
|
||||||
|
.GetAllPlugins()
|
||||||
|
.Select(x =>
|
||||||
|
new Result
|
||||||
|
{
|
||||||
|
Title = $"{x.Metadata.Name} by {x.Metadata.Author}",
|
||||||
|
SubTitle = x.Metadata.Description,
|
||||||
|
IcoPath = x.Metadata.IcoPath,
|
||||||
|
Action = e =>
|
||||||
|
{
|
||||||
|
Application.Current.MainWindow.Hide();
|
||||||
|
Uninstall(x.Metadata);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
return Search(results, uninstallSearch);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Uninstall(PluginMetadata plugin)
|
||||||
|
{
|
||||||
|
string message = string.Format(Context.API.GetTranslation("plugin_pluginsmanager_uninstall_prompt"),
|
||||||
|
Environment.NewLine, Environment.NewLine,
|
||||||
|
plugin.Name, plugin.Author);
|
||||||
|
|
||||||
|
if (MessageBox.Show(message, Context.API.GetTranslation("plugin_pluginsmanager_uninstall_title"),
|
||||||
|
MessageBoxButton.YesNo) == MessageBoxResult.Yes)
|
||||||
|
{
|
||||||
|
using var _ = File.CreateText(Path.Combine(plugin.PluginDirectory, "NeedDelete.txt"));
|
||||||
|
|
||||||
|
if (MessageBox.Show(string.Format(Context.API.GetTranslation("plugin_pluginsmanager_uninstall_successandrestart"),
|
||||||
|
plugin.Name, Environment.NewLine),
|
||||||
|
Context.API.GetTranslation("plugin_pluginsmanager_uninstall_title"),
|
||||||
|
MessageBoxButton.YesNo, MessageBoxImage.Question) == MessageBoxResult.Yes)
|
||||||
|
Context.API.RestartApp();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
11
Plugins/Flow.Launcher.Plugin.PluginsManager/Settings.cs
Normal file
11
Plugins/Flow.Launcher.Plugin.PluginsManager/Settings.cs
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace Flow.Launcher.Plugin.PluginsManager
|
||||||
|
{
|
||||||
|
internal class Settings
|
||||||
|
{
|
||||||
|
internal string UninstallHotkey { get; set; } = "uninstall";
|
||||||
|
}
|
||||||
|
}
|
||||||
61
Plugins/Flow.Launcher.Plugin.PluginsManager/Utilities.cs
Normal file
61
Plugins/Flow.Launcher.Plugin.PluginsManager/Utilities.cs
Normal file
|
|
@ -0,0 +1,61 @@
|
||||||
|
using Flow.Launcher.Infrastructure.Http;
|
||||||
|
using ICSharpCode.SharpZipLib.Zip;
|
||||||
|
using System.IO;
|
||||||
|
using System.Net;
|
||||||
|
|
||||||
|
namespace Flow.Launcher.Plugin.PluginsManager
|
||||||
|
{
|
||||||
|
internal static class Utilities
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Unzip contents to the given directory.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="zipFilePath">The path to the zip file.</param>
|
||||||
|
/// <param name="strDirectory">The output directory.</param>
|
||||||
|
/// <param name="overwrite">overwrite</param>
|
||||||
|
internal static void UnZip(string zipFilePath, string strDirectory, bool overwrite)
|
||||||
|
{
|
||||||
|
if (strDirectory == "")
|
||||||
|
strDirectory = Directory.GetCurrentDirectory();
|
||||||
|
|
||||||
|
using var zipStream = new ZipInputStream(File.OpenRead(zipFilePath));
|
||||||
|
|
||||||
|
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 var streamWriter = File.Create(destinationFile);
|
||||||
|
zipStream.CopyTo(streamWriter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static string GetContainingFolderPathAfterUnzip(string unzippedParentFolderPath)
|
||||||
|
{
|
||||||
|
var unzippedFolderCount = Directory.GetDirectories(unzippedParentFolderPath).Length;
|
||||||
|
var unzippedFilesCount = Directory.GetFiles(unzippedParentFolderPath).Length;
|
||||||
|
|
||||||
|
// adjust path depending on how the plugin is zipped up
|
||||||
|
// the recommended should be to zip up the folder not the contents
|
||||||
|
if (unzippedFolderCount == 1 && unzippedFilesCount == 0)
|
||||||
|
// folder is zipped up, unzipped plugin directory structure: tempPath/unzippedParentPluginFolder/pluginFolderName/
|
||||||
|
return Directory.GetDirectories(unzippedParentFolderPath)[0];
|
||||||
|
|
||||||
|
if (unzippedFilesCount > 1)
|
||||||
|
// content is zipped up, unzipped plugin directory structure: tempPath/unzippedParentPluginFolder/
|
||||||
|
return unzippedParentFolderPath;
|
||||||
|
|
||||||
|
return string.Empty;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
using Flow.Launcher.Infrastructure.Storage;
|
||||||
|
using Flow.Launcher.Infrastructure.UserSettings;
|
||||||
|
|
||||||
|
namespace Flow.Launcher.Plugin.PluginsManager.ViewModels
|
||||||
|
{
|
||||||
|
public class SettingsViewModel
|
||||||
|
{
|
||||||
|
private readonly PluginJsonStorage<Settings> storage;
|
||||||
|
|
||||||
|
internal Settings Settings { get; set; }
|
||||||
|
|
||||||
|
internal PluginInitContext Context { get; set; }
|
||||||
|
|
||||||
|
public SettingsViewModel(PluginInitContext context)
|
||||||
|
{
|
||||||
|
Context = context;
|
||||||
|
storage = new PluginJsonStorage<Settings>();
|
||||||
|
Settings = storage.Load();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Save()
|
||||||
|
{
|
||||||
|
storage.Save();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
<UserControl x:Class="Flow.Launcher.Plugin.PluginsManager.Views.PluginsManagerSettings"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
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>
|
||||||
|
</UserControl>
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
|
||||||
|
using Flow.Launcher.Plugin.PluginsManager.ViewModels;
|
||||||
|
|
||||||
|
namespace Flow.Launcher.Plugin.PluginsManager.Views
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Interaction logic for PluginsManagerSettings.xaml
|
||||||
|
/// </summary>
|
||||||
|
public partial class PluginsManagerSettings
|
||||||
|
{
|
||||||
|
private readonly SettingsViewModel viewModel;
|
||||||
|
|
||||||
|
public PluginsManagerSettings(SettingsViewModel viewModel)
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
|
||||||
|
this.viewModel = viewModel;
|
||||||
|
|
||||||
|
//RefreshView();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
14
Plugins/Flow.Launcher.Plugin.PluginsManager/plugin.json
Normal file
14
Plugins/Flow.Launcher.Plugin.PluginsManager/plugin.json
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
{
|
||||||
|
"ID": "9f8f9b14-2518-4907-b211-35ab6290dee7",
|
||||||
|
"ActionKeywords": [
|
||||||
|
"pm"
|
||||||
|
],
|
||||||
|
"Name": "Plugins Manager",
|
||||||
|
"Description": "Management of installing, uninstalling or updating Flow Launcher plugins",
|
||||||
|
"Author": "Jeremy Wu",
|
||||||
|
"Version": "1.0.0",
|
||||||
|
"Language": "csharp",
|
||||||
|
"Website": "https://github.com/Flow-Launcher/Flow.Launcher",
|
||||||
|
"ExecuteFileName": "Flow.Launcher.Plugin.PluginsManager.dll",
|
||||||
|
"IcoPath": "Images\\pluginsmanager.png"
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue