Skip to content

Commit e7ffd57

Browse files
Move Install/Uninstall plugin logic to Core.PluginManager
1 parent 08da4e3 commit e7ffd57

File tree

2 files changed

+120
-87
lines changed

2 files changed

+120
-87
lines changed

Flow.Launcher.Core/Plugin/PluginManager.cs

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@
1111
using Flow.Launcher.Infrastructure.UserSettings;
1212
using Flow.Launcher.Plugin;
1313
using ISavable = Flow.Launcher.Plugin.ISavable;
14+
using Flow.Launcher.Plugin.SharedCommands;
15+
using Mono.Cecil;
16+
using System.Text.Json;
1417

1518
namespace Flow.Launcher.Core.Plugin
1619
{
@@ -331,5 +334,106 @@ public static void ReplaceActionKeyword(string id, string oldActionKeyword, stri
331334
RemoveActionKeyword(id, oldActionKeyword);
332335
}
333336
}
337+
338+
private static string GetContainingFolderPathAfterUnzip(string unzippedParentFolderPath)
339+
{
340+
var unzippedFolderCount = Directory.GetDirectories(unzippedParentFolderPath).Length;
341+
var unzippedFilesCount = Directory.GetFiles(unzippedParentFolderPath).Length;
342+
343+
// adjust path depending on how the plugin is zipped up
344+
// the recommended should be to zip up the folder not the contents
345+
if (unzippedFolderCount == 1 && unzippedFilesCount == 0)
346+
// folder is zipped up, unzipped plugin directory structure: tempPath/unzippedParentPluginFolder/pluginFolderName/
347+
return Directory.GetDirectories(unzippedParentFolderPath)[0];
348+
349+
if (unzippedFilesCount > 1)
350+
// content is zipped up, unzipped plugin directory structure: tempPath/unzippedParentPluginFolder/
351+
return unzippedParentFolderPath;
352+
353+
return string.Empty;
354+
}
355+
356+
private static bool SameOrLesserPluginVersionExists(string metadataPath)
357+
{
358+
var newMetadata = JsonSerializer.Deserialize<PluginMetadata>(File.ReadAllText(metadataPath));
359+
return AllPlugins.Any(x => x.Metadata.ID == newMetadata.ID
360+
&& newMetadata.Version.CompareTo(x.Metadata.Version) <= 0);
361+
}
362+
363+
public static void Install(UserPlugin plugin, string downloadedFilePath)
364+
{
365+
var tempFolderPath = Path.Combine(Path.GetTempPath(), "flowlauncher");
366+
var tempFolderPluginPath = Path.Combine(tempFolderPath, "plugin");
367+
368+
if (Directory.Exists(tempFolderPath))
369+
Directory.Delete(tempFolderPath, true);
370+
371+
Directory.CreateDirectory(tempFolderPath);
372+
373+
var zipFilePath = Path.Combine(tempFolderPath, Path.GetFileName(downloadedFilePath));
374+
375+
File.Copy(downloadedFilePath, zipFilePath);
376+
377+
File.Delete(downloadedFilePath);
378+
379+
System.IO.Compression.ZipFile.ExtractToDirectory(zipFilePath, tempFolderPluginPath);
380+
381+
var pluginFolderPath = GetContainingFolderPathAfterUnzip(tempFolderPluginPath);
382+
383+
var metadataJsonFilePath = string.Empty;
384+
if (File.Exists(Path.Combine(pluginFolderPath, Constant.PluginMetadataFileName)))
385+
metadataJsonFilePath = Path.Combine(pluginFolderPath, Constant.PluginMetadataFileName);
386+
387+
if (string.IsNullOrEmpty(metadataJsonFilePath) || string.IsNullOrEmpty(pluginFolderPath))
388+
{
389+
throw new FileNotFoundException($"Unable to find plugin.json from the extracted zip file, or this path {pluginFolderPath} does not exist");
390+
}
391+
392+
if (SameOrLesserPluginVersionExists(metadataJsonFilePath))
393+
{
394+
throw new InvalidOperationException($"A plugin with the same ID and version already exists, or the version is greater than this downloaded plugin {plugin.Name}");
395+
}
396+
397+
var folderName = string.IsNullOrEmpty(plugin.Version) ? $"{plugin.Name}-{Guid.NewGuid()}" : $"{plugin.Name}-{plugin.Version}";
398+
399+
var defaultPluginIDs = new List<string>
400+
{
401+
"0ECADE17459B49F587BF81DC3A125110", // BrowserBookmark
402+
"CEA0FDFC6D3B4085823D60DC76F28855", // Calculator
403+
"572be03c74c642baae319fc283e561a8", // Explorer
404+
"6A122269676E40EB86EB543B945932B9", // PluginIndicator
405+
"9f8f9b14-2518-4907-b211-35ab6290dee7", // PluginsManager
406+
"b64d0a79-329a-48b0-b53f-d658318a1bf6", // ProcessKiller
407+
"791FC278BA414111B8D1886DFE447410", // Program
408+
"D409510CD0D2481F853690A07E6DC426", // Shell
409+
"CEA08895D2544B019B2E9C5009600DF4", // Sys
410+
"0308FD86DE0A4DEE8D62B9B535370992", // URL
411+
"565B73353DBF4806919830B9202EE3BF", // WebSearch
412+
"5043CETYU6A748679OPA02D27D99677A" // WindowsSettings
413+
};
414+
415+
// Treat default plugin differently, it needs to be removable along with each flow release
416+
var installDirectory = !defaultPluginIDs.Any(x => x == plugin.ID)
417+
? DataLocation.PluginsDirectory
418+
: Constant.PreinstalledDirectory;
419+
420+
var newPluginPath = Path.Combine(installDirectory, folderName);
421+
422+
FilesFolders.CopyAll(pluginFolderPath, newPluginPath);
423+
424+
Directory.Delete(pluginFolderPath, true);
425+
}
426+
427+
public static void Uninstall(PluginMetadata plugin, bool removeSettings = true)
428+
{
429+
if (removeSettings)
430+
{
431+
Settings.Plugins.Remove(plugin.ID);
432+
AllPlugins.RemoveAll(p => p.Metadata.ID == plugin.ID);
433+
}
434+
435+
// Marked for deletion. Will be deleted on next start up
436+
using var _ = File.CreateText(Path.Combine(plugin.PluginDirectory, "NeedDelete.txt"));
437+
}
334438
}
335439
}

Plugins/Flow.Launcher.Plugin.PluginsManager/PluginsManager.cs

Lines changed: 16 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
using System.IO;
1111
using System.Linq;
1212
using System.Net.Http;
13-
using System.Text.Json;
1413
using System.Threading;
1514
using System.Threading.Tasks;
1615
using System.Windows;
@@ -151,19 +150,19 @@ internal async Task InstallOrUpdateAsync(UserPlugin plugin)
151150
catch (HttpRequestException e)
152151
{
153152
Context.API.ShowMsgError(string.Format(Context.API.GetTranslation("plugin_pluginsmanager_downloading_plugin"), plugin.Name),
154-
Context.API.GetTranslation("plugin_pluginsmanager_download_error"));
153+
Context.API.GetTranslation("plugin_pluginsmanager_download_error"));
155154
Log.Exception("PluginsManager", "An error occurred while downloading plugin", e);
156155
return;
157156
}
158157
catch (Exception e)
159158
{
160159
Context.API.ShowMsgError(Context.API.GetTranslation("plugin_pluginsmanager_install_error_title"),
161-
string.Format(Context.API.GetTranslation("plugin_pluginsmanager_install_error_subtitle"),
162-
plugin.Name));
160+
string.Format(Context.API.GetTranslation("plugin_pluginsmanager_install_error_subtitle"),
161+
plugin.Name));
163162
Log.Exception("PluginsManager", "An error occurred while downloading plugin", e);
164163
return;
165164
}
166-
165+
167166
if (Settings.AutoRestartAfterChanging)
168167
{
169168
Context.API.ShowMsg(Context.API.GetTranslation("plugin_pluginsmanager_installing_plugin"),
@@ -411,77 +410,22 @@ private void Install(UserPlugin plugin, string downloadedFilePath)
411410
{
412411
if (!File.Exists(downloadedFilePath))
413412
return;
414-
415-
var tempFolderPath = Path.Combine(Path.GetTempPath(), "flowlauncher");
416-
var tempFolderPluginPath = Path.Combine(tempFolderPath, "plugin");
417-
418-
if (Directory.Exists(tempFolderPath))
419-
Directory.Delete(tempFolderPath, true);
420-
421-
Directory.CreateDirectory(tempFolderPath);
422-
423-
var zipFilePath = Path.Combine(tempFolderPath, Path.GetFileName(downloadedFilePath));
424-
425-
File.Copy(downloadedFilePath, zipFilePath);
426-
427-
File.Delete(downloadedFilePath);
428-
429-
Utilities.UnZip(zipFilePath, tempFolderPluginPath, true);
430-
431-
var pluginFolderPath = Utilities.GetContainingFolderPathAfterUnzip(tempFolderPluginPath);
432-
433-
var metadataJsonFilePath = string.Empty;
434-
if (File.Exists(Path.Combine(pluginFolderPath, Constant.PluginMetadataFileName)))
435-
metadataJsonFilePath = Path.Combine(pluginFolderPath, Constant.PluginMetadataFileName);
436-
437-
if (string.IsNullOrEmpty(metadataJsonFilePath) || string.IsNullOrEmpty(pluginFolderPath))
413+
try
414+
{
415+
PluginManager.Install(plugin, downloadedFilePath);
416+
}
417+
catch(FileNotFoundException e)
438418
{
419+
Log.Exception("Flow.Launcher.Plugin.PluginsManager", e.Message, e);
439420
MessageBox.Show(Context.API.GetTranslation("plugin_pluginsmanager_install_errormetadatafile"),
440-
Context.API.GetTranslation("plugin_pluginsmanager_install_error_title"));
441-
442-
throw new FileNotFoundException(
443-
string.Format("Unable to find plugin.json from the extracted zip file, or this path {0} does not exist", pluginFolderPath));
421+
Context.API.GetTranslation("plugin_pluginsmanager_install_error_title"));
444422
}
445-
446-
if (SameOrLesserPluginVersionExists(metadataJsonFilePath))
423+
catch(InvalidOperationException e)
447424
{
425+
Log.Exception("Flow.Launcher.Plugin.PluginsManager", e.Message, e);
448426
MessageBox.Show(string.Format(Context.API.GetTranslation("plugin_pluginsmanager_install_error_duplicate"), plugin.Name),
449-
Context.API.GetTranslation("plugin_pluginsmanager_install_error_title"));
450-
451-
throw new InvalidOperationException(
452-
string.Format("A plugin with the same ID and version already exists, " +
453-
"or the version is greater than this downloaded plugin {0}",
454-
plugin.Name));
427+
Context.API.GetTranslation("plugin_pluginsmanager_install_error_title"));
455428
}
456-
457-
var folderName = string.IsNullOrEmpty(plugin.Version) ? $"{plugin.Name}-{Guid.NewGuid()}" : $"{plugin.Name}-{plugin.Version}";
458-
459-
var defaultPluginIDs = new List<string>
460-
{
461-
"0ECADE17459B49F587BF81DC3A125110", // BrowserBookmark
462-
"CEA0FDFC6D3B4085823D60DC76F28855", // Calculator
463-
"572be03c74c642baae319fc283e561a8", // Explorer
464-
"6A122269676E40EB86EB543B945932B9", // PluginIndicator
465-
"9f8f9b14-2518-4907-b211-35ab6290dee7", // PluginsManager
466-
"b64d0a79-329a-48b0-b53f-d658318a1bf6", // ProcessKiller
467-
"791FC278BA414111B8D1886DFE447410", // Program
468-
"D409510CD0D2481F853690A07E6DC426", // Shell
469-
"CEA08895D2544B019B2E9C5009600DF4", // Sys
470-
"0308FD86DE0A4DEE8D62B9B535370992", // URL
471-
"565B73353DBF4806919830B9202EE3BF", // WebSearch
472-
"5043CETYU6A748679OPA02D27D99677A" // WindowsSettings
473-
};
474-
475-
// Treat default plugin differently, it needs to be removable along with each flow release
476-
var installDirectory = !defaultPluginIDs.Any(x => x == plugin.ID)
477-
? DataLocation.PluginsDirectory
478-
: Constant.PreinstalledDirectory;
479-
480-
var newPluginPath = Path.Combine(installDirectory, folderName);
481-
482-
FilesFolders.CopyAll(pluginFolderPath, newPluginPath);
483-
484-
Directory.Delete(pluginFolderPath, true);
485429
}
486430

487431
internal List<Result> RequestUninstall(string search)
@@ -537,24 +481,9 @@ internal List<Result> RequestUninstall(string search)
537481
return Search(results, search);
538482
}
539483

540-
private void Uninstall(PluginMetadata plugin, bool removedSetting = true)
541-
{
542-
if (removedSetting)
543-
{
544-
PluginManager.Settings.Plugins.Remove(plugin.ID);
545-
PluginManager.AllPlugins.RemoveAll(p => p.Metadata.ID == plugin.ID);
546-
}
547-
548-
// Marked for deletion. Will be deleted on next start up
549-
using var _ = File.CreateText(Path.Combine(plugin.PluginDirectory, "NeedDelete.txt"));
550-
}
551-
552-
private bool SameOrLesserPluginVersionExists(string metadataPath)
484+
private static void Uninstall(PluginMetadata plugin, bool removeSettings = true)
553485
{
554-
var newMetadata = JsonSerializer.Deserialize<PluginMetadata>(File.ReadAllText(metadataPath));
555-
return Context.API.GetAllPlugins()
556-
.Any(x => x.Metadata.ID == newMetadata.ID
557-
&& newMetadata.Version.CompareTo(x.Metadata.Version) <= 0);
486+
PluginManager.Uninstall(plugin, removeSettings);
558487
}
559488
}
560489
}

0 commit comments

Comments
 (0)