Skip to content

Support deleting plugin settings when uninstalling plugins #3219

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
Feb 23, 2025
Merged
14 changes: 12 additions & 2 deletions Flow.Launcher.Core/Plugin/JsonRPCPluginBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,10 @@ internal abstract class JsonRPCPluginBase : IAsyncPlugin, IContextMenu, ISetting
private string SettingConfigurationPath =>
Path.Combine(Context.CurrentPluginMetadata.PluginDirectory, "SettingsTemplate.yaml");

private string SettingPath => Path.Combine(DataLocation.PluginSettingsDirectory,
Context.CurrentPluginMetadata.Name, "Settings.json");
private string SettingDirectory => Path.Combine(DataLocation.PluginSettingsDirectory,
Context.CurrentPluginMetadata.Name);

private string SettingPath => Path.Combine(SettingDirectory, "Settings.json");

public abstract List<Result> LoadContextMenus(Result selectedResult);

Expand Down Expand Up @@ -159,5 +161,13 @@ public Control CreateSettingPanel()
{
return Settings.CreateSettingPanel();
}

public void DeletePluginSettingsDirectory()
{
if (Directory.Exists(SettingDirectory))
{
Directory.Delete(SettingDirectory, true);
}
}
}
}
72 changes: 64 additions & 8 deletions Flow.Launcher.Core/Plugin/PluginManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -210,9 +210,9 @@ public static async Task InitializePluginsAsync(IPublicAPI api)
{
var failed = string.Join(",", failedPlugins.Select(x => x.Metadata.Name));
API.ShowMsg(
InternationalizationManager.Instance.GetTranslation("failedToInitializePluginsTitle"),
API.GetTranslation("failedToInitializePluginsTitle"),
string.Format(
InternationalizationManager.Instance.GetTranslation("failedToInitializePluginsMessage"),
API.GetTranslation("failedToInitializePluginsMessage"),
failed
),
"",
Expand Down Expand Up @@ -439,7 +439,7 @@ public static bool PluginModified(string uuid)
public static void UpdatePlugin(PluginMetadata existingVersion, UserPlugin newVersion, string zipFilePath)
{
InstallPlugin(newVersion, zipFilePath, checkModified:false);
UninstallPlugin(existingVersion, removeSettings:false, checkModified:false);
UninstallPlugin(existingVersion, removePluginFromSettings:false, removePluginSettings:false, checkModified: false);
_modifiedPlugins.Add(existingVersion.ID);
}

Expand All @@ -454,9 +454,9 @@ public static void InstallPlugin(UserPlugin plugin, string zipFilePath)
/// <summary>
/// Uninstall a plugin.
/// </summary>
public static void UninstallPlugin(PluginMetadata plugin, bool removeSettings = true)
public static void UninstallPlugin(PluginMetadata plugin, bool removePluginFromSettings = true, bool removePluginSettings = false)
{
UninstallPlugin(plugin, removeSettings, true);
UninstallPlugin(plugin, removePluginFromSettings, removePluginSettings, true);
}

#endregion
Expand Down Expand Up @@ -521,22 +521,78 @@ internal static void InstallPlugin(UserPlugin plugin, string zipFilePath, bool c

FilesFolders.CopyAll(pluginFolderPath, newPluginPath, MessageBoxEx.Show);

Directory.Delete(tempFolderPluginPath, true);
try
{
if (Directory.Exists(tempFolderPluginPath))
Directory.Delete(tempFolderPluginPath, true);
}
catch (Exception e)
{
Log.Exception($"|PluginManager.InstallPlugin|Failed to delete temp folder {tempFolderPluginPath}", e);
}

if (checkModified)
{
_modifiedPlugins.Add(plugin.ID);
}
}

internal static void UninstallPlugin(PluginMetadata plugin, bool removeSettings, bool checkModified)
internal static void UninstallPlugin(PluginMetadata plugin, bool removePluginFromSettings, bool removePluginSettings, bool checkModified)
{
if (checkModified && PluginModified(plugin.ID))
{
throw new ArgumentException($"Plugin {plugin.Name} has been modified");
}

if (removeSettings)
if (removePluginSettings)
{
if (AllowedLanguage.IsDotNet(plugin.Language)) // for the plugin in .NET, we can use assembly loader
{
var assemblyLoader = new PluginAssemblyLoader(plugin.ExecuteFilePath);
var assembly = assemblyLoader.LoadAssemblyAndDependencies();
var assemblyName = assembly.GetName().Name;

// if user want to remove the plugin settings, we cannot call save method for the plugin json storage instance of this plugin
// so we need to remove it from the api instance
var method = API.GetType().GetMethod("RemovePluginSettings");
var pluginJsonStorage = method?.Invoke(API, new object[] { assemblyName });

// if there exists a json storage for current plugin, we need to delete the directory path
if (pluginJsonStorage != null)
{
var deleteMethod = pluginJsonStorage.GetType().GetMethod("DeleteDirectory");
try
{
deleteMethod?.Invoke(pluginJsonStorage, null);
}
catch (Exception e)
{
Log.Exception($"|PluginManager.UninstallPlugin|Failed to delete plugin json folder for {plugin.Name}", e);
API.ShowMsg(API.GetTranslation("failedToRemovePluginSettingsTitle"),
string.Format(API.GetTranslation("failedToRemovePluginSettingsMessage"), plugin.Name));
}
}
}
else // the plugin with json prc interface
{
var pluginPair = AllPlugins.FirstOrDefault(p => p.Metadata.ID == plugin.ID);
if (pluginPair != null && pluginPair.Plugin is JsonRPCPlugin jsonRpcPlugin)
{
try
{
jsonRpcPlugin.DeletePluginSettingsDirectory();
}
catch (Exception e)
{
Log.Exception($"|PluginManager.UninstallPlugin|Failed to delete plugin json folder for {plugin.Name}", e);
API.ShowMsg(API.GetTranslation("failedToRemovePluginSettingsTitle"),
string.Format(API.GetTranslation("failedToRemovePluginSettingsMessage"), plugin.Name));
}
}
}
}

if (removePluginFromSettings)
{
Settings.Plugins.Remove(plugin.ID);
AllPlugins.RemoveAll(p => p.Metadata.ID == plugin.ID);
Expand Down
18 changes: 14 additions & 4 deletions Flow.Launcher.Infrastructure/Storage/PluginJsonStorage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,17 @@

namespace Flow.Launcher.Infrastructure.Storage
{
public class PluginJsonStorage<T> :JsonStorage<T> where T : new()
public class PluginJsonStorage<T> : JsonStorage<T> where T : new()
{
// Use assembly name to check which plugin is using this storage
public readonly string AssemblyName;

public PluginJsonStorage()
{
// C# related, add python related below
var dataType = typeof(T);
var assemblyName = dataType.Assembly.GetName().Name;
DirectoryPath = Path.Combine(DataLocation.DataDirectory(), DirectoryName, Constant.Plugins, assemblyName);
AssemblyName = dataType.Assembly.GetName().Name;
DirectoryPath = Path.Combine(DataLocation.DataDirectory(), DirectoryName, Constant.Plugins, AssemblyName);
Helper.ValidateDirectory(DirectoryPath);

FilePath = Path.Combine(DirectoryPath, $"{dataType.Name}{FileSuffix}");
Expand All @@ -20,6 +23,13 @@ public PluginJsonStorage(T data) : this()
{
Data = data;
}

public void DeleteDirectory()
{
if (Directory.Exists(DirectoryPath))
{
Directory.Delete(DirectoryPath, true);
}
}
}
}

6 changes: 2 additions & 4 deletions Flow.Launcher/Languages/en.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,8 @@
<system:String x:Key="plugin_query_version">Version</system:String>
<system:String x:Key="plugin_query_web">Website</system:String>
<system:String x:Key="plugin_uninstall">Uninstall</system:String>

<system:String x:Key="failedToRemovePluginSettingsTitle">Fail to remove plugin settings</system:String>
<system:String x:Key="failedToRemovePluginSettingsMessage">Plugins: {0} - Fail to remove plugin settings files, please remove them manually</system:String>

<!-- Setting Plugin Store -->
<system:String x:Key="pluginStore">Plugin Store</system:String>
Expand All @@ -145,8 +146,6 @@
<system:String x:Key="LabelNewToolTip">This plugin has been updated within the last 7 days</system:String>
<system:String x:Key="LabelUpdateToolTip">New Update is Available</system:String>



<!-- Setting Theme -->
<system:String x:Key="theme">Theme</system:String>
<system:String x:Key="appearance">Appearance</system:String>
Expand Down Expand Up @@ -196,7 +195,6 @@
<system:String x:Key="TypeIsDarkToolTip">This theme supports two(light/dark) modes.</system:String>
<system:String x:Key="TypeHasBlurToolTip">This theme supports Blur Transparent Background.</system:String>


<!-- Setting Hotkey -->
<system:String x:Key="hotkey">Hotkey</system:String>
<system:String x:Key="hotkeys">Hotkeys</system:String>
Expand Down
17 changes: 17 additions & 0 deletions Flow.Launcher/PublicAPIInstance.cs
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,23 @@ public void LogException(string className, string message, Exception e,

private readonly ConcurrentDictionary<Type, object> _pluginJsonStorages = new();

public object RemovePluginSettings(string assemblyName)
{
foreach (var keyValuePair in _pluginJsonStorages)
{
var key = keyValuePair.Key;
var value = keyValuePair.Value;
var name = value.GetType().GetField("AssemblyName")?.GetValue(value)?.ToString();
if (name == assemblyName)
{
_pluginJsonStorages.Remove(key, out var pluginJsonStorage);
return pluginJsonStorage;
}
}

return null;
}

/// <summary>
/// Save plugin settings.
/// </summary>
Expand Down
6 changes: 4 additions & 2 deletions Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/en.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
<system:String x:Key="plugin_pluginsmanager_installing_plugin">Installing Plugin</system:String>
<system:String x:Key="plugin_pluginsmanager_install_from_web">Download and install {0}</system:String>
<system:String x:Key="plugin_pluginsmanager_uninstall_title">Plugin Uninstall</system:String>
<system:String x:Key="plugin_pluginsmanager_keep_plugin_settings_title">Keep plugin settings</system:String>
<system:String x:Key="plugin_pluginsmanager_keep_plugin_settings_subtitle">Do you want to keep the settings of the plugin for the next usage?</system:String>
<system:String x:Key="plugin_pluginsmanager_install_success_restart">Plugin {0} successfully installed. Restarting Flow, please wait...</system:String>
<system:String x:Key="plugin_pluginsmanager_install_errormetadatafile">Unable to find the plugin.json metadata file from the extracted zip file.</system:String>
<system:String x:Key="plugin_pluginsmanager_install_error_duplicate">Error: A plugin which has the same or greater version with {0} already exists.</system:String>
Expand All @@ -37,13 +39,13 @@
<system:String x:Key="plugin_pluginsmanager_update_success_restart">Plugin {0} successfully updated. Restarting Flow, please wait...</system:String>
<system:String x:Key="plugin_pluginsmanager_install_unknown_source_warning_title">Installing from an unknown source</system:String>
<system:String x:Key="plugin_pluginsmanager_install_unknown_source_warning">You are installing this plugin from an unknown source and it may contain potential risks!{0}{0}Please ensure you understand where this plugin is from and that it is safe.{0}{0}Would you like to continue still?{0}{0}(You can switch off this warning via settings)</system:String>

<system:String x:Key="plugin_pluginsmanager_install_success_no_restart">Plugin {0} successfully installed. Please restart Flow.</system:String>
<system:String x:Key="plugin_pluginsmanager_uninstall_success_no_restart">Plugin {0} successfully uninstalled. Please restart Flow.</system:String>
<system:String x:Key="plugin_pluginsmanager_update_success_no_restart">Plugin {0} successfully updated. Please restart Flow.</system:String>
<system:String x:Key="plugin_pluginsmanager_update_all_success_no_restart">{0} plugins successfully updated. Please restart Flow.</system:String>
<system:String x:Key="plugin_pluginsmanager_plugin_modified_error">Plugin {0} has already been modified. Please restart Flow before making any further changes.</system:String>

<!-- 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>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -682,7 +682,11 @@ private void Uninstall(PluginMetadata plugin)
{
try
{
PluginManager.UninstallPlugin(plugin, removeSettings: true);
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;
PluginManager.UninstallPlugin(plugin, removePluginFromSettings: true, removePluginSettings: removePluginSettings);
}
catch (ArgumentException e)
{
Expand Down
Loading