2025-04-04 08:33:03 +00:00
|
|
|
|
using System;
|
2020-12-06 08:58:27 +00:00
|
|
|
|
using System.Collections.Generic;
|
2020-12-07 10:21:14 +00:00
|
|
|
|
using System.IO;
|
2020-12-06 20:40:42 +00:00
|
|
|
|
using System.Linq;
|
2021-11-20 10:26:56 +00:00
|
|
|
|
using System.Net.Http;
|
2021-01-17 10:39:53 +00:00
|
|
|
|
using System.Threading;
|
2020-12-28 15:06:47 +00:00
|
|
|
|
using System.Threading.Tasks;
|
2020-12-07 10:21:14 +00:00
|
|
|
|
using System.Windows;
|
2025-04-04 08:33:03 +00:00
|
|
|
|
using Flow.Launcher.Plugin.SharedCommands;
|
2020-12-06 08:58:27 +00:00
|
|
|
|
|
|
|
|
|
|
namespace Flow.Launcher.Plugin.PluginsManager
|
|
|
|
|
|
{
|
|
|
|
|
|
internal class PluginsManager
|
|
|
|
|
|
{
|
2025-04-04 07:37:03 +00:00
|
|
|
|
private const string ZipSuffix = "zip";
|
|
|
|
|
|
|
|
|
|
|
|
private static readonly string ClassName = nameof(PluginsManager);
|
2021-11-20 10:26:56 +00:00
|
|
|
|
|
2020-12-10 10:28:01 +00:00
|
|
|
|
private PluginInitContext Context { get; set; }
|
2020-12-06 20:42:23 +00:00
|
|
|
|
|
2020-12-14 10:05:32 +00:00
|
|
|
|
private Settings Settings { get; set; }
|
|
|
|
|
|
|
2020-12-17 10:07:47 +00:00
|
|
|
|
private bool shouldHideWindow = true;
|
2020-12-27 13:13:25 +00:00
|
|
|
|
|
|
|
|
|
|
private bool ShouldHideWindow
|
2020-12-17 10:07:47 +00:00
|
|
|
|
{
|
|
|
|
|
|
set { shouldHideWindow = value; }
|
|
|
|
|
|
get
|
|
|
|
|
|
{
|
|
|
|
|
|
var setValue = shouldHideWindow;
|
|
|
|
|
|
// Default value for hide main window is true. Revert after get call.
|
|
|
|
|
|
// This ensures when set by another method to false, it is only used once.
|
|
|
|
|
|
shouldHideWindow = true;
|
|
|
|
|
|
|
|
|
|
|
|
return setValue;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-01-17 10:39:53 +00:00
|
|
|
|
internal readonly string icoPath = "Images\\pluginsmanager.png";
|
2020-12-06 20:42:23 +00:00
|
|
|
|
|
2020-12-14 10:05:32 +00:00
|
|
|
|
internal PluginsManager(PluginInitContext context, Settings settings)
|
2020-12-06 20:40:42 +00:00
|
|
|
|
{
|
2020-12-10 10:28:01 +00:00
|
|
|
|
Context = context;
|
2020-12-14 10:05:32 +00:00
|
|
|
|
Settings = settings;
|
2020-12-06 20:40:42 +00:00
|
|
|
|
}
|
2020-12-27 13:13:25 +00:00
|
|
|
|
|
2020-12-29 04:14:59 +00:00
|
|
|
|
internal List<Result> GetDefaultHotKeys()
|
|
|
|
|
|
{
|
|
|
|
|
|
return new List<Result>()
|
|
|
|
|
|
{
|
2025-04-04 08:33:03 +00:00
|
|
|
|
new()
|
2020-12-29 04:14:59 +00:00
|
|
|
|
{
|
2022-01-12 21:48:16 +00:00
|
|
|
|
Title = Settings.InstallCommand,
|
2020-12-29 04:14:59 +00:00
|
|
|
|
IcoPath = icoPath,
|
2022-01-12 21:48:16 +00:00
|
|
|
|
AutoCompleteText = $"{Context.CurrentPluginMetadata.ActionKeyword} {Settings.InstallCommand} ",
|
2020-12-29 04:14:59 +00:00
|
|
|
|
Action = _ =>
|
|
|
|
|
|
{
|
2024-01-15 22:49:46 +00:00
|
|
|
|
Context.API.ChangeQuery(
|
|
|
|
|
|
$"{Context.CurrentPluginMetadata.ActionKeyword} {Settings.InstallCommand} ");
|
2020-12-29 04:14:59 +00:00
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
2025-04-04 08:33:03 +00:00
|
|
|
|
new()
|
2021-01-17 10:39:53 +00:00
|
|
|
|
{
|
2022-01-12 21:48:16 +00:00
|
|
|
|
Title = Settings.UninstallCommand,
|
2021-01-17 10:39:53 +00:00
|
|
|
|
IcoPath = icoPath,
|
2022-01-12 21:48:16 +00:00
|
|
|
|
AutoCompleteText = $"{Context.CurrentPluginMetadata.ActionKeyword} {Settings.UninstallCommand} ",
|
2021-01-17 10:39:53 +00:00
|
|
|
|
Action = _ =>
|
2020-12-29 04:14:59 +00:00
|
|
|
|
{
|
2024-01-15 22:49:46 +00:00
|
|
|
|
Context.API.ChangeQuery(
|
|
|
|
|
|
$"{Context.CurrentPluginMetadata.ActionKeyword} {Settings.UninstallCommand} ");
|
2021-01-17 10:39:53 +00:00
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
2025-04-04 08:33:03 +00:00
|
|
|
|
new()
|
2021-01-17 10:39:53 +00:00
|
|
|
|
{
|
2022-01-12 21:48:16 +00:00
|
|
|
|
Title = Settings.UpdateCommand,
|
2021-01-17 10:39:53 +00:00
|
|
|
|
IcoPath = icoPath,
|
2022-01-12 21:48:16 +00:00
|
|
|
|
AutoCompleteText = $"{Context.CurrentPluginMetadata.ActionKeyword} {Settings.UpdateCommand} ",
|
2021-01-17 10:39:53 +00:00
|
|
|
|
Action = _ =>
|
2020-12-29 04:14:59 +00:00
|
|
|
|
{
|
2024-01-15 22:49:46 +00:00
|
|
|
|
Context.API.ChangeQuery(
|
|
|
|
|
|
$"{Context.CurrentPluginMetadata.ActionKeyword} {Settings.UpdateCommand} ");
|
2021-01-17 10:39:53 +00:00
|
|
|
|
return false;
|
2020-12-29 04:14:59 +00:00
|
|
|
|
}
|
2021-01-17 10:39:53 +00:00
|
|
|
|
}
|
|
|
|
|
|
};
|
2020-12-29 04:14:59 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2022-08-10 03:18:37 +00:00
|
|
|
|
internal async Task InstallOrUpdateAsync(UserPlugin plugin)
|
2020-12-06 08:58:27 +00:00
|
|
|
|
{
|
2021-11-20 09:01:57 +00:00
|
|
|
|
if (PluginExists(plugin.ID))
|
2020-12-06 08:58:27 +00:00
|
|
|
|
{
|
2020-12-27 13:13:25 +00:00
|
|
|
|
if (Context.API.GetAllPlugins()
|
2020-12-29 10:48:55 +00:00
|
|
|
|
.Any(x => x.Metadata.ID == plugin.ID && x.Metadata.Version.CompareTo(plugin.Version) < 0))
|
2020-12-17 10:37:38 +00:00
|
|
|
|
{
|
2024-05-16 11:22:08 +00:00
|
|
|
|
var updateDetail = !plugin.IsFromLocalInstallPath ? plugin.Name : plugin.LocalInstallPath;
|
|
|
|
|
|
|
|
|
|
|
|
Context
|
|
|
|
|
|
.API
|
|
|
|
|
|
.ChangeQuery(
|
|
|
|
|
|
$"{Context.CurrentPluginMetadata.ActionKeywords.FirstOrDefault()} {Settings.UpdateCommand} {updateDetail}");
|
2020-12-17 10:37:38 +00:00
|
|
|
|
|
2021-08-02 21:58:35 +00:00
|
|
|
|
var mainWindow = Application.Current.MainWindow;
|
2021-11-27 02:51:45 +00:00
|
|
|
|
mainWindow.Show();
|
2021-08-02 21:58:35 +00:00
|
|
|
|
mainWindow.Focus();
|
|
|
|
|
|
|
2020-12-17 10:37:38 +00:00
|
|
|
|
shouldHideWindow = false;
|
|
|
|
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Context.API.ShowMsg(Context.API.GetTranslation("plugin_pluginsmanager_update_alreadyexists"));
|
2020-12-10 20:45:01 +00:00
|
|
|
|
return;
|
2020-12-06 08:58:27 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-01 04:34:48 +00:00
|
|
|
|
if (Context.API.PluginModified(plugin.ID))
|
|
|
|
|
|
{
|
2025-07-01 05:18:26 +00:00
|
|
|
|
Context.API.ShowMsgError(
|
|
|
|
|
|
string.Format(Context.API.GetTranslation("plugin_pluginsmanager_plugin_modified_error_title"), plugin.Name),
|
|
|
|
|
|
Context.API.GetTranslation("plugin_pluginsmanager_plugin_modified_error_message"));
|
2025-07-01 04:34:48 +00:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-09-29 05:06:42 +00:00
|
|
|
|
string message;
|
|
|
|
|
|
if (Settings.AutoRestartAfterChanging)
|
|
|
|
|
|
{
|
|
|
|
|
|
message = string.Format(Context.API.GetTranslation("plugin_pluginsmanager_install_prompt"),
|
2024-01-15 22:49:46 +00:00
|
|
|
|
plugin.Name, plugin.Author,
|
|
|
|
|
|
Environment.NewLine, Environment.NewLine);
|
2023-09-29 05:06:42 +00:00
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
message = string.Format(Context.API.GetTranslation("plugin_pluginsmanager_install_prompt_no_restart"),
|
2024-01-15 22:49:46 +00:00
|
|
|
|
plugin.Name, plugin.Author,
|
|
|
|
|
|
Environment.NewLine);
|
2023-09-29 05:06:42 +00:00
|
|
|
|
}
|
2020-12-10 11:58:18 +00:00
|
|
|
|
|
2024-11-27 04:21:34 +00:00
|
|
|
|
if (Context.API.ShowMsgBox(message, Context.API.GetTranslation("plugin_pluginsmanager_install_title"),
|
2024-11-26 13:31:13 +00:00
|
|
|
|
MessageBoxButton.YesNo) == MessageBoxResult.No)
|
2020-12-10 11:58:18 +00:00
|
|
|
|
return;
|
|
|
|
|
|
|
2021-11-20 09:01:57 +00:00
|
|
|
|
// 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)
|
2021-12-19 17:35:34 +00:00
|
|
|
|
? $"{plugin.Name}-{Guid.NewGuid()}.zip"
|
|
|
|
|
|
: $"{plugin.Name}-{plugin.Version}.zip";
|
2021-11-20 09:01:57 +00:00
|
|
|
|
|
2023-11-11 08:11:21 +00:00
|
|
|
|
var filePath = Path.Combine(Path.GetTempPath(), downloadFilename);
|
2020-12-14 10:05:32 +00:00
|
|
|
|
|
2020-12-07 10:21:14 +00:00
|
|
|
|
try
|
2020-12-06 08:58:27 +00:00
|
|
|
|
{
|
2025-01-11 05:24:41 +00:00
|
|
|
|
using var cts = new CancellationTokenSource();
|
|
|
|
|
|
|
2024-05-16 11:22:08 +00:00
|
|
|
|
if (!plugin.IsFromLocalInstallPath)
|
2023-11-12 03:42:13 +00:00
|
|
|
|
{
|
2025-02-07 06:15:01 +00:00
|
|
|
|
await DownloadFileAsync(
|
2025-01-19 07:44:26 +00:00
|
|
|
|
$"{Context.API.GetTranslation("plugin_pluginsmanager_downloading_plugin")} {plugin.Name}",
|
|
|
|
|
|
plugin.UrlDownload, filePath, cts);
|
2024-05-16 11:22:08 +00:00
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
filePath = plugin.LocalInstallPath;
|
2025-01-05 13:08:23 +00:00
|
|
|
|
}
|
2025-01-05 13:16:49 +00:00
|
|
|
|
|
|
|
|
|
|
// check if user cancelled download before installing plugin
|
2025-01-11 05:24:41 +00:00
|
|
|
|
if (cts.IsCancellationRequested)
|
2025-01-05 13:16:49 +00:00
|
|
|
|
return;
|
2025-01-06 13:16:28 +00:00
|
|
|
|
else
|
2025-07-01 04:34:48 +00:00
|
|
|
|
if (!Install(plugin, filePath))
|
|
|
|
|
|
return;
|
2025-01-05 13:08:07 +00:00
|
|
|
|
}
|
2023-11-10 16:06:02 +00:00
|
|
|
|
catch (HttpRequestException e)
|
|
|
|
|
|
{
|
2025-01-05 13:08:23 +00:00
|
|
|
|
// show error message
|
|
|
|
|
|
Context.API.ShowMsgError(
|
|
|
|
|
|
string.Format(Context.API.GetTranslation("plugin_pluginsmanager_downloading_plugin"), plugin.Name),
|
|
|
|
|
|
Context.API.GetTranslation("plugin_pluginsmanager_download_error"));
|
2025-04-04 07:37:03 +00:00
|
|
|
|
Context.API.LogException(ClassName, "An error occurred while downloading plugin", e);
|
2025-01-05 13:08:23 +00:00
|
|
|
|
|
2023-11-10 16:06:02 +00:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
2020-12-10 02:29:47 +00:00
|
|
|
|
catch (Exception e)
|
2020-12-07 10:21:14 +00:00
|
|
|
|
{
|
2025-01-05 13:08:23 +00:00
|
|
|
|
// show error message
|
|
|
|
|
|
Context.API.ShowMsgError(Context.API.GetTranslation("plugin_pluginsmanager_install_error_title"),
|
|
|
|
|
|
string.Format(Context.API.GetTranslation("plugin_pluginsmanager_install_error_subtitle"),
|
|
|
|
|
|
plugin.Name));
|
2025-04-04 07:37:03 +00:00
|
|
|
|
Context.API.LogException(ClassName, "An error occurred while downloading plugin", e);
|
2025-01-05 13:08:23 +00:00
|
|
|
|
|
2021-01-01 10:54:34 +00:00
|
|
|
|
return;
|
2020-12-06 08:58:27 +00:00
|
|
|
|
}
|
2023-11-22 13:25:17 +00:00
|
|
|
|
|
2023-09-29 05:06:42 +00:00
|
|
|
|
if (Settings.AutoRestartAfterChanging)
|
|
|
|
|
|
{
|
|
|
|
|
|
Context.API.ShowMsg(Context.API.GetTranslation("plugin_pluginsmanager_installing_plugin"),
|
2024-01-15 22:49:46 +00:00
|
|
|
|
string.Format(Context.API.GetTranslation("plugin_pluginsmanager_install_success_restart"),
|
|
|
|
|
|
plugin.Name));
|
2023-09-29 05:06:42 +00:00
|
|
|
|
Context.API.RestartApp();
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
Context.API.ShowMsg(Context.API.GetTranslation("plugin_pluginsmanager_installing_plugin"),
|
2024-01-15 22:49:46 +00:00
|
|
|
|
string.Format(Context.API.GetTranslation("plugin_pluginsmanager_install_success_no_restart"),
|
|
|
|
|
|
plugin.Name));
|
2023-09-29 05:06:42 +00:00
|
|
|
|
}
|
2020-12-06 08:58:27 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-07 11:35:12 +00:00
|
|
|
|
private async Task DownloadFileAsync(string progressBoxTitle, string downloadUrl, string filePath, CancellationTokenSource cts, bool deleteFile = true, bool showProgress = true)
|
2025-01-19 07:44:26 +00:00
|
|
|
|
{
|
2025-02-07 06:15:01 +00:00
|
|
|
|
if (deleteFile && File.Exists(filePath))
|
2025-01-19 07:44:26 +00:00
|
|
|
|
File.Delete(filePath);
|
|
|
|
|
|
|
2025-02-07 06:15:01 +00:00
|
|
|
|
if (showProgress)
|
|
|
|
|
|
{
|
|
|
|
|
|
var exceptionHappened = false;
|
2025-07-07 11:35:12 +00:00
|
|
|
|
await Context.API.ShowProgressBoxAsync(progressBoxTitle,
|
2025-02-07 06:15:01 +00:00
|
|
|
|
async (reportProgress) =>
|
2025-01-19 07:44:26 +00:00
|
|
|
|
{
|
2025-02-07 06:15:01 +00:00
|
|
|
|
if (reportProgress == null)
|
|
|
|
|
|
{
|
2025-06-29 10:20:47 +00:00
|
|
|
|
// when reportProgress is null, it means there is exception with the progress box
|
2025-02-07 06:15:01 +00:00
|
|
|
|
// so we record it with exceptionHappened and return so that progress box will close instantly
|
|
|
|
|
|
exceptionHappened = true;
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
await Context.API.HttpDownloadAsync(downloadUrl, filePath, reportProgress, cts.Token).ConfigureAwait(false);
|
|
|
|
|
|
}
|
|
|
|
|
|
}, cts.Cancel);
|
2025-01-19 07:44:26 +00:00
|
|
|
|
|
2025-02-07 06:15:01 +00:00
|
|
|
|
// if exception happened while downloading and user does not cancel downloading,
|
|
|
|
|
|
// we need to redownload the plugin
|
|
|
|
|
|
if (exceptionHappened && (!cts.IsCancellationRequested))
|
|
|
|
|
|
await Context.API.HttpDownloadAsync(downloadUrl, filePath, token: cts.Token).ConfigureAwait(false);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
await Context.API.HttpDownloadAsync(downloadUrl, filePath, token: cts.Token).ConfigureAwait(false);
|
|
|
|
|
|
}
|
2025-01-19 07:44:26 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2024-01-15 22:49:46 +00:00
|
|
|
|
internal async ValueTask<List<Result>> RequestUpdateAsync(string search, CancellationToken token,
|
|
|
|
|
|
bool usePrimaryUrlOnly = false)
|
2020-12-06 08:58:27 +00:00
|
|
|
|
{
|
2025-04-04 08:05:33 +00:00
|
|
|
|
await Context.API.UpdatePluginManifestAsync(usePrimaryUrlOnly, token);
|
2021-01-29 03:37:26 +00:00
|
|
|
|
|
2024-05-16 11:22:08 +00:00
|
|
|
|
var pluginFromLocalPath = null as UserPlugin;
|
|
|
|
|
|
var updateFromLocalPath = false;
|
2024-02-04 16:29:05 +00:00
|
|
|
|
|
2024-05-16 11:22:08 +00:00
|
|
|
|
if (FilesFolders.IsZipFilePath(search, checkFileExists: true))
|
|
|
|
|
|
{
|
|
|
|
|
|
pluginFromLocalPath = Utilities.GetPluginInfoFromZip(search);
|
2025-06-29 08:24:21 +00:00
|
|
|
|
|
|
|
|
|
|
if (pluginFromLocalPath == null) return new List<Result>
|
|
|
|
|
|
{
|
|
|
|
|
|
new()
|
|
|
|
|
|
{
|
|
|
|
|
|
Title = Context.API.GetTranslation("plugin_pluginsmanager_invalid_zip_title"),
|
|
|
|
|
|
SubTitle = string.Format(Context.API.GetTranslation("plugin_pluginsmanager_invalid_zip_subtitle"),
|
|
|
|
|
|
search),
|
|
|
|
|
|
IcoPath = icoPath
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2024-05-16 11:22:08 +00:00
|
|
|
|
pluginFromLocalPath.LocalInstallPath = search;
|
|
|
|
|
|
updateFromLocalPath = true;
|
2024-10-29 05:23:54 +00:00
|
|
|
|
}
|
2024-02-04 16:29:05 +00:00
|
|
|
|
|
2024-05-16 11:22:08 +00:00
|
|
|
|
var updateSource = !updateFromLocalPath
|
2025-04-04 08:33:03 +00:00
|
|
|
|
? Context.API.GetPluginManifest()
|
2024-10-29 05:23:54 +00:00
|
|
|
|
: new List<UserPlugin> { pluginFromLocalPath };
|
2024-02-04 16:29:05 +00:00
|
|
|
|
|
2024-01-15 22:49:46 +00:00
|
|
|
|
var resultsForUpdate = (
|
2020-12-27 13:13:25 +00:00
|
|
|
|
from existingPlugin in Context.API.GetAllPlugins()
|
2024-05-16 11:22:08 +00:00
|
|
|
|
join pluginUpdateSource in updateSource
|
|
|
|
|
|
on existingPlugin.Metadata.ID equals pluginUpdateSource.ID
|
|
|
|
|
|
where string.Compare(existingPlugin.Metadata.Version, pluginUpdateSource.Version,
|
2024-01-15 22:49:46 +00:00
|
|
|
|
StringComparison.InvariantCulture) <
|
2024-05-16 11:22:08 +00:00
|
|
|
|
0 // if current version precedes version of the plugin from update source (e.g. PluginsManifest)
|
2025-04-04 08:33:03 +00:00
|
|
|
|
&& !Context.API.PluginModified(existingPlugin.Metadata.ID)
|
2020-12-27 13:13:25 +00:00
|
|
|
|
select
|
|
|
|
|
|
new
|
|
|
|
|
|
{
|
2025-07-01 04:34:48 +00:00
|
|
|
|
existingPlugin.Metadata.ID,
|
2024-05-16 11:22:08 +00:00
|
|
|
|
pluginUpdateSource.Name,
|
|
|
|
|
|
pluginUpdateSource.Author,
|
2020-12-27 13:13:25 +00:00
|
|
|
|
CurrentVersion = existingPlugin.Metadata.Version,
|
2024-05-16 11:22:08 +00:00
|
|
|
|
NewVersion = pluginUpdateSource.Version,
|
2020-12-27 13:13:25 +00:00
|
|
|
|
existingPlugin.Metadata.IcoPath,
|
|
|
|
|
|
PluginExistingMetadata = existingPlugin.Metadata,
|
2024-05-16 11:22:08 +00:00
|
|
|
|
PluginNewUserPlugin = pluginUpdateSource
|
2024-01-15 22:49:46 +00:00
|
|
|
|
}).ToList();
|
2020-12-17 09:37:01 +00:00
|
|
|
|
|
|
|
|
|
|
if (!resultsForUpdate.Any())
|
2020-12-27 13:13:25 +00:00
|
|
|
|
return new List<Result>
|
|
|
|
|
|
{
|
2025-04-04 08:33:03 +00:00
|
|
|
|
new()
|
2020-12-27 13:13:25 +00:00
|
|
|
|
{
|
|
|
|
|
|
Title = Context.API.GetTranslation("plugin_pluginsmanager_update_noresult_title"),
|
|
|
|
|
|
SubTitle = Context.API.GetTranslation("plugin_pluginsmanager_update_noresult_subtitle"),
|
|
|
|
|
|
IcoPath = icoPath
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
2020-12-17 09:37:01 +00:00
|
|
|
|
|
|
|
|
|
|
var results = resultsForUpdate
|
2020-12-27 13:13:25 +00:00
|
|
|
|
.Select(x =>
|
|
|
|
|
|
new Result
|
|
|
|
|
|
{
|
|
|
|
|
|
Title = $"{x.Name} by {x.Author}",
|
|
|
|
|
|
SubTitle = $"Update from version {x.CurrentVersion} to {x.NewVersion}",
|
|
|
|
|
|
IcoPath = x.IcoPath,
|
|
|
|
|
|
Action = e =>
|
|
|
|
|
|
{
|
2025-07-01 04:34:48 +00:00
|
|
|
|
if (Context.API.PluginModified(x.ID))
|
|
|
|
|
|
{
|
2025-07-01 05:18:26 +00:00
|
|
|
|
Context.API.ShowMsgError(
|
|
|
|
|
|
string.Format(Context.API.GetTranslation("plugin_pluginsmanager_plugin_modified_error_title"), x.Name),
|
|
|
|
|
|
Context.API.GetTranslation("plugin_pluginsmanager_plugin_modified_error_message"));
|
2025-07-01 04:34:48 +00:00
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-09-29 05:06:42 +00:00
|
|
|
|
string message;
|
|
|
|
|
|
if (Settings.AutoRestartAfterChanging)
|
|
|
|
|
|
{
|
2024-01-15 22:49:46 +00:00
|
|
|
|
message = string.Format(
|
|
|
|
|
|
Context.API.GetTranslation("plugin_pluginsmanager_update_prompt"),
|
|
|
|
|
|
x.Name, x.Author,
|
|
|
|
|
|
Environment.NewLine, Environment.NewLine);
|
2023-09-29 05:06:42 +00:00
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
2024-01-15 22:49:46 +00:00
|
|
|
|
message = string.Format(
|
|
|
|
|
|
Context.API.GetTranslation("plugin_pluginsmanager_update_prompt_no_restart"),
|
|
|
|
|
|
x.Name, x.Author,
|
|
|
|
|
|
Environment.NewLine);
|
2023-09-29 05:06:42 +00:00
|
|
|
|
}
|
2020-12-27 13:13:25 +00:00
|
|
|
|
|
2024-11-27 04:21:34 +00:00
|
|
|
|
if (Context.API.ShowMsgBox(message,
|
2021-12-19 17:35:34 +00:00
|
|
|
|
Context.API.GetTranslation("plugin_pluginsmanager_update_title"),
|
2024-11-26 13:31:13 +00:00
|
|
|
|
MessageBoxButton.YesNo) != MessageBoxResult.Yes)
|
2020-12-27 13:13:25 +00:00
|
|
|
|
{
|
2024-01-15 22:49:46 +00:00
|
|
|
|
return false;
|
|
|
|
|
|
}
|
2020-12-27 13:13:25 +00:00
|
|
|
|
|
2024-01-15 22:49:46 +00:00
|
|
|
|
var downloadToFilePath = Path.Combine(Path.GetTempPath(),
|
|
|
|
|
|
$"{x.Name}-{x.NewVersion}.zip");
|
|
|
|
|
|
|
2025-06-04 05:44:50 +00:00
|
|
|
|
_ = Task.Run(async () =>
|
2024-01-15 22:49:46 +00:00
|
|
|
|
{
|
2025-06-04 05:44:50 +00:00
|
|
|
|
try
|
2024-05-16 11:22:08 +00:00
|
|
|
|
{
|
2025-06-04 05:44:50 +00:00
|
|
|
|
using var cts = new CancellationTokenSource();
|
2024-10-29 05:23:54 +00:00
|
|
|
|
|
2025-06-04 05:44:50 +00:00
|
|
|
|
if (!x.PluginNewUserPlugin.IsFromLocalInstallPath)
|
|
|
|
|
|
{
|
|
|
|
|
|
await DownloadFileAsync(
|
|
|
|
|
|
$"{Context.API.GetTranslation("plugin_pluginsmanager_downloading_plugin")} {x.PluginNewUserPlugin.Name}",
|
|
|
|
|
|
x.PluginNewUserPlugin.UrlDownload, downloadToFilePath, cts);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
downloadToFilePath = x.PluginNewUserPlugin.LocalInstallPath;
|
|
|
|
|
|
}
|
2025-01-19 07:44:26 +00:00
|
|
|
|
|
2025-06-04 05:44:50 +00:00
|
|
|
|
// check if user cancelled download before installing plugin
|
|
|
|
|
|
if (cts.IsCancellationRequested)
|
2025-01-19 07:44:26 +00:00
|
|
|
|
{
|
2025-06-04 05:44:50 +00:00
|
|
|
|
return;
|
2025-01-19 07:44:26 +00:00
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
2025-07-01 01:05:21 +00:00
|
|
|
|
if (!await Context.API.UpdatePluginAsync(x.PluginExistingMetadata, x.PluginNewUserPlugin,
|
|
|
|
|
|
downloadToFilePath))
|
|
|
|
|
|
{
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
2025-06-04 05:44:50 +00:00
|
|
|
|
|
|
|
|
|
|
if (Settings.AutoRestartAfterChanging)
|
|
|
|
|
|
{
|
|
|
|
|
|
Context.API.ShowMsg(
|
|
|
|
|
|
Context.API.GetTranslation("plugin_pluginsmanager_update_title"),
|
|
|
|
|
|
string.Format(
|
|
|
|
|
|
Context.API.GetTranslation(
|
|
|
|
|
|
"plugin_pluginsmanager_update_success_restart"),
|
|
|
|
|
|
x.Name));
|
|
|
|
|
|
Context.API.RestartApp();
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
Context.API.ShowMsg(
|
|
|
|
|
|
Context.API.GetTranslation("plugin_pluginsmanager_update_title"),
|
|
|
|
|
|
string.Format(
|
|
|
|
|
|
Context.API.GetTranslation(
|
|
|
|
|
|
"plugin_pluginsmanager_update_success_no_restart"),
|
|
|
|
|
|
x.Name));
|
|
|
|
|
|
}
|
2025-01-19 07:44:26 +00:00
|
|
|
|
}
|
2024-01-15 22:49:46 +00:00
|
|
|
|
}
|
2025-06-04 05:44:50 +00:00
|
|
|
|
catch (HttpRequestException e)
|
|
|
|
|
|
{
|
|
|
|
|
|
// show error message
|
|
|
|
|
|
Context.API.ShowMsgError(
|
|
|
|
|
|
string.Format(Context.API.GetTranslation("plugin_pluginsmanager_downloading_plugin"), x.Name),
|
|
|
|
|
|
Context.API.GetTranslation("plugin_pluginsmanager_download_error"));
|
|
|
|
|
|
Context.API.LogException(ClassName, "An error occurred while downloading plugin", e);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Exception e)
|
|
|
|
|
|
{
|
|
|
|
|
|
// show error message
|
|
|
|
|
|
Context.API.LogException(ClassName, $"Update failed for {x.Name}", e);
|
|
|
|
|
|
Context.API.ShowMsgError(
|
|
|
|
|
|
Context.API.GetTranslation("plugin_pluginsmanager_install_error_title"),
|
|
|
|
|
|
string.Format(
|
|
|
|
|
|
Context.API.GetTranslation("plugin_pluginsmanager_install_error_subtitle"),
|
|
|
|
|
|
x.Name));
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
2020-12-27 13:13:25 +00:00
|
|
|
|
|
2024-01-15 22:49:46 +00:00
|
|
|
|
return true;
|
2021-07-27 10:32:53 +00:00
|
|
|
|
},
|
2021-12-19 17:35:34 +00:00
|
|
|
|
ContextData =
|
2021-07-27 10:32:53 +00:00
|
|
|
|
new UserPlugin
|
|
|
|
|
|
{
|
2023-11-22 13:25:17 +00:00
|
|
|
|
Website = x.PluginNewUserPlugin.Website,
|
|
|
|
|
|
UrlSourceCode = x.PluginNewUserPlugin.UrlSourceCode
|
2021-07-27 10:32:53 +00:00
|
|
|
|
}
|
2020-12-27 13:13:25 +00:00
|
|
|
|
});
|
2020-12-17 09:37:01 +00:00
|
|
|
|
|
2024-01-15 22:49:46 +00:00
|
|
|
|
// Update all result
|
2025-01-04 13:55:44 +00:00
|
|
|
|
if (resultsForUpdate.Count > 1)
|
2023-11-19 16:07:29 +00:00
|
|
|
|
{
|
|
|
|
|
|
var updateAllResult = new Result
|
|
|
|
|
|
{
|
|
|
|
|
|
Title = Context.API.GetTranslation("plugin_pluginsmanager_update_all_title"),
|
|
|
|
|
|
SubTitle = Context.API.GetTranslation("plugin_pluginsmanager_update_all_subtitle"),
|
|
|
|
|
|
IcoPath = icoPath,
|
2024-01-03 08:29:56 +00:00
|
|
|
|
AsyncAction = async e =>
|
2023-11-19 16:07:29 +00:00
|
|
|
|
{
|
2025-07-01 04:34:48 +00:00
|
|
|
|
if (resultsForUpdate.All(x => Context.API.PluginModified(x.ID)))
|
|
|
|
|
|
{
|
|
|
|
|
|
Context.API.ShowMsgError(Context.API.GetTranslation("plugin_pluginsmanager_install_error_title"),
|
|
|
|
|
|
string.Format(Context.API.GetTranslation("plugin_pluginsmanager_plugin_modified_error"),
|
|
|
|
|
|
string.Join(" ", resultsForUpdate.Select(x => x.Name))));
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-11-22 13:25:17 +00:00
|
|
|
|
string message;
|
2023-11-22 13:49:43 +00:00
|
|
|
|
if (Settings.AutoRestartAfterChanging)
|
2023-11-22 13:25:17 +00:00
|
|
|
|
{
|
2024-01-15 22:49:46 +00:00
|
|
|
|
message = string.Format(
|
|
|
|
|
|
Context.API.GetTranslation("plugin_pluginsmanager_update_all_prompt"),
|
2025-01-04 13:55:44 +00:00
|
|
|
|
resultsForUpdate.Count, Environment.NewLine);
|
2023-11-22 13:25:17 +00:00
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
2024-01-15 22:49:46 +00:00
|
|
|
|
message = string.Format(
|
|
|
|
|
|
Context.API.GetTranslation("plugin_pluginsmanager_update_all_prompt_no_restart"),
|
2025-01-04 13:55:44 +00:00
|
|
|
|
resultsForUpdate.Count);
|
2023-11-22 13:25:17 +00:00
|
|
|
|
}
|
2023-11-22 13:49:43 +00:00
|
|
|
|
|
2024-11-27 04:21:34 +00:00
|
|
|
|
if (Context.API.ShowMsgBox(message,
|
2024-01-15 22:49:46 +00:00
|
|
|
|
Context.API.GetTranslation("plugin_pluginsmanager_update_title"),
|
2024-11-26 13:31:13 +00:00
|
|
|
|
MessageBoxButton.YesNo) == MessageBoxResult.No)
|
2023-11-22 13:25:17 +00:00
|
|
|
|
{
|
2023-12-29 10:17:46 +00:00
|
|
|
|
return false;
|
|
|
|
|
|
}
|
2023-11-22 13:25:17 +00:00
|
|
|
|
|
2025-07-01 04:34:48 +00:00
|
|
|
|
var anyPluginSuccess = false;
|
2024-01-03 08:29:56 +00:00
|
|
|
|
await Task.WhenAll(resultsForUpdate.Select(async plugin =>
|
2023-12-29 10:17:46 +00:00
|
|
|
|
{
|
2024-01-15 22:49:46 +00:00
|
|
|
|
var downloadToFilePath = Path.Combine(Path.GetTempPath(),
|
|
|
|
|
|
$"{plugin.Name}-{plugin.NewVersion}.zip");
|
2023-11-22 13:25:17 +00:00
|
|
|
|
|
2024-01-03 08:29:56 +00:00
|
|
|
|
try
|
2023-12-29 10:17:46 +00:00
|
|
|
|
{
|
2025-01-19 07:44:26 +00:00
|
|
|
|
using var cts = new CancellationTokenSource();
|
2023-11-22 13:25:17 +00:00
|
|
|
|
|
2025-02-07 06:15:01 +00:00
|
|
|
|
await DownloadFileAsync(
|
2025-01-19 07:44:26 +00:00
|
|
|
|
$"{Context.API.GetTranslation("plugin_pluginsmanager_downloading_plugin")} {plugin.PluginNewUserPlugin.Name}",
|
|
|
|
|
|
plugin.PluginNewUserPlugin.UrlDownload, downloadToFilePath, cts);
|
2023-11-22 13:25:17 +00:00
|
|
|
|
|
2025-01-19 07:44:26 +00:00
|
|
|
|
// check if user cancelled download before installing plugin
|
|
|
|
|
|
if (cts.IsCancellationRequested)
|
|
|
|
|
|
return;
|
|
|
|
|
|
else
|
2025-07-01 01:05:21 +00:00
|
|
|
|
if (!await Context.API.UpdatePluginAsync(plugin.PluginExistingMetadata, plugin.PluginNewUserPlugin,
|
|
|
|
|
|
downloadToFilePath))
|
|
|
|
|
|
return;
|
2025-07-01 04:34:48 +00:00
|
|
|
|
|
|
|
|
|
|
anyPluginSuccess = true;
|
2024-01-03 08:29:56 +00:00
|
|
|
|
}
|
|
|
|
|
|
catch (Exception ex)
|
2023-11-22 13:49:43 +00:00
|
|
|
|
{
|
2025-04-04 07:37:03 +00:00
|
|
|
|
Context.API.LogException(ClassName, $"Update failed for {plugin.Name}", ex.InnerException);
|
2025-06-04 05:44:10 +00:00
|
|
|
|
Context.API.ShowMsgError(
|
2023-12-29 10:17:46 +00:00
|
|
|
|
Context.API.GetTranslation("plugin_pluginsmanager_install_error_title"),
|
|
|
|
|
|
string.Format(
|
|
|
|
|
|
Context.API.GetTranslation("plugin_pluginsmanager_install_error_subtitle"),
|
|
|
|
|
|
plugin.Name));
|
2024-01-03 08:29:56 +00:00
|
|
|
|
}
|
|
|
|
|
|
}));
|
2023-11-22 13:49:43 +00:00
|
|
|
|
|
2025-07-01 04:34:48 +00:00
|
|
|
|
if (!anyPluginSuccess) return false;
|
|
|
|
|
|
|
2023-12-29 10:17:46 +00:00
|
|
|
|
if (Settings.AutoRestartAfterChanging)
|
|
|
|
|
|
{
|
|
|
|
|
|
Context.API.ShowMsg(Context.API.GetTranslation("plugin_pluginsmanager_update_title"),
|
2024-01-15 22:49:46 +00:00
|
|
|
|
string.Format(
|
|
|
|
|
|
Context.API.GetTranslation("plugin_pluginsmanager_update_all_success_restart"),
|
2025-01-04 13:55:44 +00:00
|
|
|
|
resultsForUpdate.Count));
|
2023-12-29 10:17:46 +00:00
|
|
|
|
Context.API.RestartApp();
|
2023-11-22 13:25:17 +00:00
|
|
|
|
}
|
2023-12-29 10:17:46 +00:00
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
Context.API.ShowMsg(Context.API.GetTranslation("plugin_pluginsmanager_update_title"),
|
2024-01-15 22:49:46 +00:00
|
|
|
|
string.Format(
|
|
|
|
|
|
Context.API.GetTranslation("plugin_pluginsmanager_update_all_success_no_restart"),
|
2025-01-04 13:55:44 +00:00
|
|
|
|
resultsForUpdate.Count));
|
2023-12-29 10:17:46 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return true;
|
2023-11-19 16:07:29 +00:00
|
|
|
|
},
|
|
|
|
|
|
ContextData = new UserPlugin()
|
|
|
|
|
|
};
|
|
|
|
|
|
results = results.Prepend(updateAllResult);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-05-16 11:22:08 +00:00
|
|
|
|
return !updateFromLocalPath ? Search(results, search) : results.ToList();
|
2020-12-06 08:58:27 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2020-12-10 11:58:18 +00:00
|
|
|
|
internal bool PluginExists(string id)
|
2020-12-06 08:58:27 +00:00
|
|
|
|
{
|
2020-12-10 11:58:18 +00:00
|
|
|
|
return Context.API.GetAllPlugins().Any(x => x.Metadata.ID == id);
|
2020-12-06 08:58:27 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2020-12-20 09:08:52 +00:00
|
|
|
|
internal List<Result> Search(IEnumerable<Result> results, string searchName)
|
2020-12-10 10:28:01 +00:00
|
|
|
|
{
|
|
|
|
|
|
if (string.IsNullOrEmpty(searchName))
|
2020-12-20 09:08:52 +00:00
|
|
|
|
return results.ToList();
|
2020-12-10 10:28:01 +00:00
|
|
|
|
|
|
|
|
|
|
return results
|
2020-12-27 13:13:25 +00:00
|
|
|
|
.Where(x =>
|
|
|
|
|
|
{
|
2025-04-04 07:37:03 +00:00
|
|
|
|
var matchResult = Context.API.FuzzySearch(searchName, x.Title);
|
2020-12-27 13:13:25 +00:00
|
|
|
|
if (matchResult.IsSearchPrecisionScoreMet())
|
|
|
|
|
|
x.Score = matchResult.Score;
|
2020-12-14 08:14:50 +00:00
|
|
|
|
|
2020-12-27 13:13:25 +00:00
|
|
|
|
return matchResult.IsSearchPrecisionScoreMet();
|
|
|
|
|
|
})
|
|
|
|
|
|
.ToList();
|
2020-12-10 10:28:01 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2021-11-18 15:35:45 +00:00
|
|
|
|
internal List<Result> InstallFromWeb(string url)
|
|
|
|
|
|
{
|
2021-11-20 10:26:56 +00:00
|
|
|
|
var filename = url.Split("/").Last();
|
2025-04-04 07:37:03 +00:00
|
|
|
|
var name = filename.Split(string.Format(".{0}", ZipSuffix)).First();
|
2021-11-20 10:26:56 +00:00
|
|
|
|
|
2021-11-18 15:35:45 +00:00
|
|
|
|
var plugin = new UserPlugin
|
|
|
|
|
|
{
|
|
|
|
|
|
ID = "",
|
2021-11-20 10:26:56 +00:00
|
|
|
|
Name = name,
|
2021-11-20 09:01:57 +00:00
|
|
|
|
Version = string.Empty,
|
2021-11-20 10:26:56 +00:00
|
|
|
|
Author = Context.API.GetTranslation("plugin_pluginsmanager_unknown_author"),
|
2021-11-18 15:35:45 +00:00
|
|
|
|
UrlDownload = url
|
|
|
|
|
|
};
|
2021-11-20 10:26:56 +00:00
|
|
|
|
|
2021-11-18 15:35:45 +00:00
|
|
|
|
var result = new Result
|
|
|
|
|
|
{
|
2021-11-21 05:09:49 +00:00
|
|
|
|
Title = string.Format(Context.API.GetTranslation("plugin_pluginsmanager_install_from_web"), filename),
|
|
|
|
|
|
SubTitle = plugin.UrlDownload,
|
2021-11-18 16:40:06 +00:00
|
|
|
|
IcoPath = icoPath,
|
2021-11-18 15:35:45 +00:00
|
|
|
|
Action = e =>
|
|
|
|
|
|
{
|
|
|
|
|
|
if (e.SpecialKeyState.CtrlPressed)
|
|
|
|
|
|
{
|
2022-01-03 13:25:38 +00:00
|
|
|
|
SearchWeb.OpenInBrowserTab(plugin.UrlDownload);
|
2021-11-18 15:35:45 +00:00
|
|
|
|
return ShouldHideWindow;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-11-21 05:09:49 +00:00
|
|
|
|
if (Settings.WarnFromUnknownSource)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!InstallSourceKnown(plugin.UrlDownload)
|
2024-11-27 04:21:34 +00:00
|
|
|
|
&& Context.API.ShowMsgBox(string.Format(
|
2024-01-15 22:49:46 +00:00
|
|
|
|
Context.API.GetTranslation("plugin_pluginsmanager_install_unknown_source_warning"),
|
2021-12-19 17:35:34 +00:00
|
|
|
|
Environment.NewLine),
|
2024-01-15 22:49:46 +00:00
|
|
|
|
Context.API.GetTranslation(
|
|
|
|
|
|
"plugin_pluginsmanager_install_unknown_source_warning_title"),
|
2024-11-26 13:31:13 +00:00
|
|
|
|
MessageBoxButton.YesNo) == MessageBoxResult.No)
|
2021-11-21 05:09:49 +00:00
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-02-18 03:41:26 +00:00
|
|
|
|
Context.API.HideMainWindow();
|
2022-08-10 03:18:37 +00:00
|
|
|
|
_ = InstallOrUpdateAsync(plugin);
|
2021-12-19 17:35:34 +00:00
|
|
|
|
|
2021-11-18 15:35:45 +00:00
|
|
|
|
return ShouldHideWindow;
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
2021-11-20 10:26:56 +00:00
|
|
|
|
|
2024-01-15 22:49:46 +00:00
|
|
|
|
return new List<Result> { result };
|
2021-11-18 15:35:45 +00:00
|
|
|
|
}
|
2021-11-21 05:09:49 +00:00
|
|
|
|
|
2024-05-16 11:22:08 +00:00
|
|
|
|
internal List<Result> InstallFromLocalPath(string localPath)
|
2024-02-01 15:38:44 +00:00
|
|
|
|
{
|
2024-05-16 11:22:08 +00:00
|
|
|
|
var plugin = Utilities.GetPluginInfoFromZip(localPath);
|
2024-02-01 15:38:44 +00:00
|
|
|
|
|
2025-06-29 08:24:21 +00:00
|
|
|
|
if (plugin == null)
|
|
|
|
|
|
{
|
|
|
|
|
|
return new List<Result>
|
|
|
|
|
|
{
|
|
|
|
|
|
new()
|
|
|
|
|
|
{
|
|
|
|
|
|
Title = Context.API.GetTranslation("plugin_pluginsmanager_invalid_zip_title"),
|
|
|
|
|
|
SubTitle = string.Format(Context.API.GetTranslation("plugin_pluginsmanager_invalid_zip_subtitle"),
|
|
|
|
|
|
localPath),
|
|
|
|
|
|
IcoPath = icoPath
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-05-16 11:22:08 +00:00
|
|
|
|
plugin.LocalInstallPath = localPath;
|
2024-02-01 15:38:44 +00:00
|
|
|
|
|
2024-05-16 11:22:08 +00:00
|
|
|
|
return new List<Result>
|
2024-02-01 15:38:44 +00:00
|
|
|
|
{
|
2025-04-04 08:33:03 +00:00
|
|
|
|
new()
|
2024-02-01 15:38:44 +00:00
|
|
|
|
{
|
2024-05-16 11:22:08 +00:00
|
|
|
|
Title = $"{plugin.Name} by {plugin.Author}",
|
|
|
|
|
|
SubTitle = plugin.Description,
|
|
|
|
|
|
IcoPath = plugin.IcoPath,
|
|
|
|
|
|
Action = e =>
|
2024-02-01 15:38:44 +00:00
|
|
|
|
{
|
2024-05-16 11:22:08 +00:00
|
|
|
|
if (Settings.WarnFromUnknownSource)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!InstallSourceKnown(plugin.Website)
|
2024-11-27 04:21:34 +00:00
|
|
|
|
&& Context.API.ShowMsgBox(string.Format(
|
2024-10-29 05:23:54 +00:00
|
|
|
|
Context.API.GetTranslation(
|
|
|
|
|
|
"plugin_pluginsmanager_install_unknown_source_warning"),
|
2024-05-16 11:22:08 +00:00
|
|
|
|
Environment.NewLine),
|
|
|
|
|
|
Context.API.GetTranslation(
|
|
|
|
|
|
"plugin_pluginsmanager_install_unknown_source_warning_title"),
|
2024-11-26 13:31:13 +00:00
|
|
|
|
MessageBoxButton.YesNo) == MessageBoxResult.No)
|
2024-05-16 11:22:08 +00:00
|
|
|
|
return false;
|
|
|
|
|
|
}
|
2024-02-01 15:38:44 +00:00
|
|
|
|
|
2025-02-18 03:41:26 +00:00
|
|
|
|
Context.API.HideMainWindow();
|
2024-05-16 11:22:08 +00:00
|
|
|
|
_ = InstallOrUpdateAsync(plugin);
|
2024-02-01 15:38:44 +00:00
|
|
|
|
|
2024-05-16 11:22:08 +00:00
|
|
|
|
return ShouldHideWindow;
|
|
|
|
|
|
}
|
2024-02-01 15:38:44 +00:00
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-11-21 05:09:49 +00:00
|
|
|
|
private bool InstallSourceKnown(string url)
|
|
|
|
|
|
{
|
2024-10-29 05:23:54 +00:00
|
|
|
|
var pieces = url.Split('/');
|
|
|
|
|
|
|
|
|
|
|
|
if (pieces.Length < 4)
|
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
|
|
var author = pieces[3];
|
2025-06-29 09:06:29 +00:00
|
|
|
|
var acceptedHost = "github.com";
|
2021-11-21 05:09:49 +00:00
|
|
|
|
var acceptedSource = "https://github.com";
|
2023-08-21 21:58:46 +00:00
|
|
|
|
var constructedUrlPart = string.Format("{0}/{1}/", acceptedSource, author);
|
2021-11-21 05:09:49 +00:00
|
|
|
|
|
2025-06-29 09:06:29 +00:00
|
|
|
|
if (!Uri.TryCreate(url, UriKind.Absolute, out var uri) || uri.Host != acceptedHost)
|
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
|
|
return Context.API.GetAllPlugins().Any(x =>
|
|
|
|
|
|
!string.IsNullOrEmpty(x.Metadata.Website) &&
|
|
|
|
|
|
x.Metadata.Website.StartsWith(constructedUrlPart)
|
|
|
|
|
|
);
|
2021-11-21 05:09:49 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-01-04 13:55:44 +00:00
|
|
|
|
internal async ValueTask<List<Result>> RequestInstallOrUpdateAsync(string search, CancellationToken token,
|
2024-01-15 22:49:46 +00:00
|
|
|
|
bool usePrimaryUrlOnly = false)
|
2020-12-06 20:40:42 +00:00
|
|
|
|
{
|
2025-04-04 08:05:33 +00:00
|
|
|
|
await Context.API.UpdatePluginManifestAsync(usePrimaryUrlOnly, token);
|
2021-01-17 10:46:08 +00:00
|
|
|
|
|
2022-01-11 06:55:59 +00:00
|
|
|
|
if (Uri.IsWellFormedUriString(search, UriKind.Absolute)
|
2025-04-04 07:37:03 +00:00
|
|
|
|
&& search.Split('.').Last() == ZipSuffix)
|
2022-01-11 06:55:59 +00:00
|
|
|
|
return InstallFromWeb(search);
|
2021-11-20 10:26:56 +00:00
|
|
|
|
|
2024-05-16 11:22:08 +00:00
|
|
|
|
if (FilesFolders.IsZipFilePath(search, checkFileExists: true))
|
|
|
|
|
|
return InstallFromLocalPath(search);
|
2024-02-01 15:38:44 +00:00
|
|
|
|
|
2020-12-14 10:05:32 +00:00
|
|
|
|
var results =
|
2025-04-04 08:33:03 +00:00
|
|
|
|
Context.API.GetPluginManifest()
|
|
|
|
|
|
.Where(x => !PluginExists(x.ID) && !Context.API.PluginModified(x.ID))
|
2020-12-27 13:13:25 +00:00
|
|
|
|
.Select(x =>
|
|
|
|
|
|
new Result
|
2020-12-06 20:40:42 +00:00
|
|
|
|
{
|
2020-12-27 13:13:25 +00:00
|
|
|
|
Title = $"{x.Name} by {x.Author}",
|
|
|
|
|
|
SubTitle = x.Description,
|
2023-11-10 16:48:18 +00:00
|
|
|
|
IcoPath = x.IcoPath,
|
2020-12-27 13:13:25 +00:00
|
|
|
|
Action = e =>
|
|
|
|
|
|
{
|
2021-05-14 18:17:57 +00:00
|
|
|
|
if (e.SpecialKeyState.CtrlPressed)
|
|
|
|
|
|
{
|
2022-01-03 13:25:38 +00:00
|
|
|
|
SearchWeb.OpenInBrowserTab(x.Website);
|
2021-05-14 18:17:57 +00:00
|
|
|
|
return ShouldHideWindow;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-02-18 03:41:26 +00:00
|
|
|
|
Context.API.HideMainWindow();
|
2022-08-10 03:18:37 +00:00
|
|
|
|
_ = InstallOrUpdateAsync(x); // No need to wait
|
2020-12-27 13:13:25 +00:00
|
|
|
|
return ShouldHideWindow;
|
|
|
|
|
|
},
|
|
|
|
|
|
ContextData = x
|
|
|
|
|
|
});
|
2020-12-06 20:40:42 +00:00
|
|
|
|
|
2022-01-11 06:55:59 +00:00
|
|
|
|
return Search(results, search);
|
2020-12-06 20:40:42 +00:00
|
|
|
|
}
|
2020-12-08 10:59:45 +00:00
|
|
|
|
|
2025-07-01 04:34:48 +00:00
|
|
|
|
private bool Install(UserPlugin plugin, string downloadedFilePath)
|
2020-12-08 10:59:45 +00:00
|
|
|
|
{
|
2020-12-10 11:58:18 +00:00
|
|
|
|
if (!File.Exists(downloadedFilePath))
|
2024-01-15 22:49:46 +00:00
|
|
|
|
throw new FileNotFoundException($"Plugin {plugin.ID} zip file not found at {downloadedFilePath}",
|
|
|
|
|
|
downloadedFilePath);
|
2024-05-16 11:22:08 +00:00
|
|
|
|
|
2023-11-10 16:40:27 +00:00
|
|
|
|
try
|
|
|
|
|
|
{
|
2025-07-01 01:05:21 +00:00
|
|
|
|
if (!Context.API.InstallPlugin(plugin, downloadedFilePath))
|
2025-07-01 04:34:48 +00:00
|
|
|
|
return false;
|
2024-10-29 05:23:54 +00:00
|
|
|
|
|
2024-05-16 11:22:08 +00:00
|
|
|
|
if (!plugin.IsFromLocalInstallPath)
|
2024-02-04 16:29:05 +00:00
|
|
|
|
File.Delete(downloadedFilePath);
|
2025-07-01 04:34:48 +00:00
|
|
|
|
|
|
|
|
|
|
return true;
|
2023-11-10 16:40:27 +00:00
|
|
|
|
}
|
2023-11-11 07:05:24 +00:00
|
|
|
|
catch (FileNotFoundException e)
|
2020-12-10 11:58:18 +00:00
|
|
|
|
{
|
2023-11-11 07:05:24 +00:00
|
|
|
|
Context.API.ShowMsgError(Context.API.GetTranslation("plugin_pluginsmanager_install_error_title"),
|
2024-01-15 22:49:46 +00:00
|
|
|
|
Context.API.GetTranslation("plugin_pluginsmanager_install_errormetadatafile"));
|
2025-04-04 07:37:03 +00:00
|
|
|
|
Context.API.LogException(ClassName, e.Message, e);
|
2020-12-10 11:58:18 +00:00
|
|
|
|
}
|
2023-11-11 07:05:24 +00:00
|
|
|
|
catch (InvalidOperationException e)
|
2021-11-21 02:31:43 +00:00
|
|
|
|
{
|
2023-11-11 07:05:24 +00:00
|
|
|
|
Context.API.ShowMsgError(Context.API.GetTranslation("plugin_pluginsmanager_install_error_title"),
|
2024-01-15 22:49:46 +00:00
|
|
|
|
string.Format(Context.API.GetTranslation("plugin_pluginsmanager_install_error_duplicate"),
|
|
|
|
|
|
plugin.Name));
|
2025-04-04 07:37:03 +00:00
|
|
|
|
Context.API.LogException(ClassName, e.Message, e);
|
2023-11-11 07:05:24 +00:00
|
|
|
|
}
|
2023-11-22 13:25:17 +00:00
|
|
|
|
catch (ArgumentException e)
|
|
|
|
|
|
{
|
2023-11-11 07:05:24 +00:00
|
|
|
|
Context.API.ShowMsgError(Context.API.GetTranslation("plugin_pluginsmanager_install_error_title"),
|
2024-01-15 22:49:46 +00:00
|
|
|
|
string.Format(Context.API.GetTranslation("plugin_pluginsmanager_plugin_modified_error"),
|
|
|
|
|
|
plugin.Name));
|
2025-04-04 07:37:03 +00:00
|
|
|
|
Context.API.LogException(ClassName, e.Message, e);
|
2021-11-21 02:31:43 +00:00
|
|
|
|
}
|
2025-07-01 04:34:48 +00:00
|
|
|
|
|
|
|
|
|
|
return false;
|
2020-12-08 10:59:45 +00:00
|
|
|
|
}
|
2020-12-10 10:28:01 +00:00
|
|
|
|
|
|
|
|
|
|
internal List<Result> RequestUninstall(string search)
|
|
|
|
|
|
{
|
2020-12-20 09:08:52 +00:00
|
|
|
|
var results = Context.API
|
2020-12-27 13:13:25 +00:00
|
|
|
|
.GetAllPlugins()
|
|
|
|
|
|
.Select(x =>
|
|
|
|
|
|
new Result
|
|
|
|
|
|
{
|
|
|
|
|
|
Title = $"{x.Metadata.Name} by {x.Metadata.Author}",
|
|
|
|
|
|
SubTitle = x.Metadata.Description,
|
|
|
|
|
|
IcoPath = x.Metadata.IcoPath,
|
2025-03-06 12:15:49 +00:00
|
|
|
|
AsyncAction = async e =>
|
2020-12-27 13:13:25 +00:00
|
|
|
|
{
|
2025-07-01 05:18:26 +00:00
|
|
|
|
if (Context.API.PluginModified(x.Metadata.ID))
|
|
|
|
|
|
{
|
|
|
|
|
|
Context.API.ShowMsgError(
|
|
|
|
|
|
string.Format(Context.API.GetTranslation("plugin_pluginsmanager_plugin_modified_error_title"), x.Metadata.Name),
|
|
|
|
|
|
Context.API.GetTranslation("plugin_pluginsmanager_plugin_modified_error_message"));
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-09-29 05:06:42 +00:00
|
|
|
|
string message;
|
|
|
|
|
|
if (Settings.AutoRestartAfterChanging)
|
|
|
|
|
|
{
|
2024-01-15 22:49:46 +00:00
|
|
|
|
message = string.Format(
|
|
|
|
|
|
Context.API.GetTranslation("plugin_pluginsmanager_uninstall_prompt"),
|
|
|
|
|
|
x.Metadata.Name, x.Metadata.Author,
|
|
|
|
|
|
Environment.NewLine, Environment.NewLine);
|
2023-09-29 05:06:42 +00:00
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
2024-01-15 22:49:46 +00:00
|
|
|
|
message = string.Format(
|
|
|
|
|
|
Context.API.GetTranslation("plugin_pluginsmanager_uninstall_prompt_no_restart"),
|
|
|
|
|
|
x.Metadata.Name, x.Metadata.Author,
|
|
|
|
|
|
Environment.NewLine);
|
2023-09-29 05:06:42 +00:00
|
|
|
|
}
|
2020-12-27 13:13:25 +00:00
|
|
|
|
|
2024-11-27 04:21:34 +00:00
|
|
|
|
if (Context.API.ShowMsgBox(message,
|
2021-12-19 17:35:34 +00:00
|
|
|
|
Context.API.GetTranslation("plugin_pluginsmanager_uninstall_title"),
|
2024-11-26 13:31:13 +00:00
|
|
|
|
MessageBoxButton.YesNo) == MessageBoxResult.Yes)
|
2020-12-27 13:13:25 +00:00
|
|
|
|
{
|
2025-02-18 03:41:26 +00:00
|
|
|
|
Context.API.HideMainWindow();
|
2025-07-01 04:34:48 +00:00
|
|
|
|
if (!await UninstallAsync(x.Metadata))
|
|
|
|
|
|
{
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
2023-09-29 05:06:42 +00:00
|
|
|
|
if (Settings.AutoRestartAfterChanging)
|
|
|
|
|
|
{
|
|
|
|
|
|
Context.API.RestartApp();
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
2024-01-15 22:49:46 +00:00
|
|
|
|
Context.API.ShowMsg(
|
|
|
|
|
|
Context.API.GetTranslation("plugin_pluginsmanager_uninstall_title"),
|
|
|
|
|
|
string.Format(
|
|
|
|
|
|
Context.API.GetTranslation(
|
|
|
|
|
|
"plugin_pluginsmanager_uninstall_success_no_restart"),
|
|
|
|
|
|
x.Metadata.Name));
|
2023-09-29 05:06:42 +00:00
|
|
|
|
}
|
2020-12-27 13:13:25 +00:00
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
2020-12-10 10:28:01 +00:00
|
|
|
|
|
2022-01-11 06:55:59 +00:00
|
|
|
|
return Search(results, search);
|
2020-12-10 10:28:01 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-01 04:34:48 +00:00
|
|
|
|
private async Task<bool> UninstallAsync(PluginMetadata plugin)
|
2021-11-21 02:31:43 +00:00
|
|
|
|
{
|
2023-11-11 07:05:24 +00:00
|
|
|
|
try
|
|
|
|
|
|
{
|
2025-02-01 05:10:32 +00:00
|
|
|
|
var removePluginSettings = Context.API.ShowMsgBox(
|
|
|
|
|
|
Context.API.GetTranslation("plugin_pluginsmanager_keep_plugin_settings_subtitle"),
|
|
|
|
|
|
Context.API.GetTranslation("plugin_pluginsmanager_keep_plugin_settings_title"),
|
|
|
|
|
|
button: MessageBoxButton.YesNo) == MessageBoxResult.No;
|
2025-07-01 04:34:48 +00:00
|
|
|
|
return await Context.API.UninstallPluginAsync(plugin, removePluginSettings);
|
2023-11-11 07:05:24 +00:00
|
|
|
|
}
|
|
|
|
|
|
catch (ArgumentException e)
|
|
|
|
|
|
{
|
2025-04-04 07:37:03 +00:00
|
|
|
|
Context.API.LogException(ClassName, e.Message, e);
|
2023-11-11 07:05:24 +00:00
|
|
|
|
Context.API.ShowMsgError(Context.API.GetTranslation("plugin_pluginsmanager_uninstall_error_title"),
|
2025-07-01 04:00:43 +00:00
|
|
|
|
string.Format(Context.API.GetTranslation("plugin_pluginsmanager_plugin_modified_error"), plugin.Name));
|
2025-07-01 04:34:48 +00:00
|
|
|
|
return false;
|
2023-11-11 07:05:24 +00:00
|
|
|
|
}
|
2021-11-21 02:31:43 +00:00
|
|
|
|
}
|
2020-12-06 08:58:27 +00:00
|
|
|
|
}
|
2022-08-10 03:18:37 +00:00
|
|
|
|
}
|