diff --git a/Flow.Launcher.Core/Configuration/Portable.cs b/Flow.Launcher.Core/Configuration/Portable.cs index c46a6102da2..44e4434bed7 100644 --- a/Flow.Launcher.Core/Configuration/Portable.cs +++ b/Flow.Launcher.Core/Configuration/Portable.cs @@ -32,10 +32,9 @@ public void DisablePortableMode() try { MoveUserDataFolder(DataLocation.PortableDataPath, DataLocation.RoamingDataPath); -#if DEBUG +#if !DEBUG // Create shortcuts and uninstaller are not required in debug mode, // otherwise will repoint the path of the actual installed production version to the debug version -#else CreateShortcuts(); CreateUninstallerEntry(); #endif @@ -48,10 +47,7 @@ public void DisablePortableMode() } catch (Exception e) { -#if !DEBUG - Log.Exception("Portable", "Error occured while disabling portable mode", e); -#endif - throw; + Log.Exception("|Portable.DisablePortableMode|Error occured while disabling portable mode", e); } } @@ -60,10 +56,9 @@ public void EnablePortableMode() try { MoveUserDataFolder(DataLocation.RoamingDataPath, DataLocation.PortableDataPath); -#if DEBUG +#if !DEBUG // Remove shortcuts and uninstaller are not required in debug mode, // otherwise will delete the actual installed production version -#else RemoveShortcuts(); RemoveUninstallerEntry(); #endif @@ -76,10 +71,7 @@ public void EnablePortableMode() } catch (Exception e) { -#if !DEBUG - Log.Exception("Portable", "Error occured while enabling portable mode", e); -#endif - throw; + Log.Exception("|Portable.EnablePortableMode|Error occured while enabling portable mode", e); } } @@ -125,14 +117,13 @@ public void CreateShortcuts() public void CreateUninstallerEntry() { var uninstallRegSubKey = @"Software\Microsoft\Windows\CurrentVersion\Uninstall"; - // NB: Sometimes the Uninstall key doesn't exist - using (var parentKey = - RegistryKey.OpenBaseKey(RegistryHive.CurrentUser, RegistryView.Default) - .CreateSubKey("Uninstall", RegistryKeyPermissionCheck.ReadWriteSubTree)) {; } - var key = RegistryKey.OpenBaseKey(RegistryHive.CurrentUser, RegistryView.Default) - .CreateSubKey(uninstallRegSubKey + "\\" + Constant.FlowLauncher, RegistryKeyPermissionCheck.ReadWriteSubTree); - key.SetValue("DisplayIcon", Constant.ApplicationDirectory + "\\app.ico", RegistryValueKind.String); + using (var baseKey = RegistryKey.OpenBaseKey(RegistryHive.CurrentUser, RegistryView.Default)) + using (var subKey1 = baseKey.CreateSubKey(uninstallRegSubKey, RegistryKeyPermissionCheck.ReadWriteSubTree)) + using (var subKey2 = subKey1.CreateSubKey(Constant.FlowLauncher, RegistryKeyPermissionCheck.ReadWriteSubTree)) + { + subKey2.SetValue("DisplayIcon", Path.Combine(Constant.ApplicationDirectory, "app.ico"), RegistryValueKind.String); + } using (var portabilityUpdater = NewUpdateManager()) { @@ -142,7 +133,10 @@ public void CreateUninstallerEntry() internal void IndicateDeletion(string filePathTodelete) { - using (StreamWriter sw = File.CreateText(filePathTodelete + "\\" + DataLocation.DeletionIndicatorFile)){} + var deleteFilePath = Path.Combine(filePathTodelete, DataLocation.DeletionIndicatorFile); + using (var _ = File.CreateText(deleteFilePath)) + { + } } /// @@ -152,21 +146,18 @@ internal void IndicateDeletion(string filePathTodelete) public void PreStartCleanUpAfterPortabilityUpdate() { // Specify here so this method does not rely on other environment variables to initialise - var portableDataPath = Path.Combine(Directory.GetParent(Assembly.GetExecutingAssembly().Location.NonNull()).ToString(), "UserData"); - var roamingDataPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "FlowLauncher"); - - bool DataLocationPortableDeleteRequired = false; - bool DataLocationRoamingDeleteRequired = false; - - if ((roamingDataPath + "\\" + DataLocation.DeletionIndicatorFile).FileExits()) - DataLocationRoamingDeleteRequired = true; + var portableDataDir = Path.Combine(Directory.GetParent(Assembly.GetExecutingAssembly().Location.NonNull()).ToString(), "UserData"); + var roamingDataDir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "FlowLauncher"); - if ((portableDataPath + "\\" + DataLocation.DeletionIndicatorFile).FileExits()) - DataLocationPortableDeleteRequired = true; + // Get full path to the .dead files for each case + var portableDataDeleteFilePath = Path.Combine(portableDataDir, DataLocation.DeletionIndicatorFile); + var roamingDataDeleteFilePath = Path.Combine(roamingDataDir, DataLocation.DeletionIndicatorFile); - if (DataLocationRoamingDeleteRequired) + // If the data folder in %appdata% is marked for deletion, + // delete it and prompt the user to pick the portable data location + if (File.Exists(roamingDataDeleteFilePath)) { - FilesFolders.RemoveFolderIfExists(roamingDataPath); + FilesFolders.RemoveFolderIfExists(roamingDataDir); if (MessageBox.Show("Flow Launcher has detected you enabled portable mode, " + "would you like to move it to a different location?", string.Empty, @@ -176,18 +167,15 @@ public void PreStartCleanUpAfterPortabilityUpdate() Environment.Exit(0); } - - return; } - - if(DataLocationPortableDeleteRequired) + // Otherwise, if the portable data folder is marked for deletion, + // delete it and notify the user about it. + else if (File.Exists(portableDataDeleteFilePath)) { - FilesFolders.RemoveFolderIfExists(portableDataPath); + FilesFolders.RemoveFolderIfExists(portableDataDir); MessageBox.Show("Flow Launcher has detected you disabled portable mode, " + "the relevant shortcuts and uninstaller entry have been created"); - - return; } } @@ -196,7 +184,7 @@ public bool CanUpdatePortability() var roamingLocationExists = DataLocation.RoamingDataPath.LocationExists(); var portableLocationExists = DataLocation.PortableDataPath.LocationExists(); - if(roamingLocationExists && portableLocationExists) + if (roamingLocationExists && portableLocationExists) { MessageBox.Show(string.Format("Flow Launcher detected your user data exists both in {0} and " + "{1}. {2}{2}Please delete {1} in order to proceed. No changes have occured.", diff --git a/Flow.Launcher.Core/Plugin/PluginConfig.cs b/Flow.Launcher.Core/Plugin/PluginConfig.cs index d12dee0c438..b946fa44d21 100644 --- a/Flow.Launcher.Core/Plugin/PluginConfig.cs +++ b/Flow.Launcher.Core/Plugin/PluginConfig.cs @@ -2,9 +2,8 @@ using System.Collections.Generic; using System.Linq; using System.IO; -using System.Threading.Tasks; using Newtonsoft.Json; -using Flow.Launcher.Infrastructure.Exception; +using Flow.Launcher.Infrastructure; using Flow.Launcher.Infrastructure.Logger; using Flow.Launcher.Plugin; @@ -13,24 +12,16 @@ namespace Flow.Launcher.Core.Plugin internal abstract class PluginConfig { - private const string PluginConfigName = "plugin.json"; - private static readonly List PluginMetadatas = new List(); - /// - /// Parse plugin metadata in giving directories + /// Parse plugin metadata in the given directories /// /// /// public static List Parse(string[] pluginDirectories) { - PluginMetadatas.Clear(); + var allPluginMetadata = new List(); var directories = pluginDirectories.SelectMany(Directory.GetDirectories); - ParsePluginConfigs(directories); - return PluginMetadatas; - } - private static void ParsePluginConfigs(IEnumerable directories) - { // todo use linq when diable plugin is implmented since parallel.foreach + list is not thread saft foreach (var directory in directories) { @@ -50,15 +41,17 @@ private static void ParsePluginConfigs(IEnumerable directories) PluginMetadata metadata = GetPluginMetadata(directory); if (metadata != null) { - PluginMetadatas.Add(metadata); + allPluginMetadata.Add(metadata); } } } + + return allPluginMetadata; } private static PluginMetadata GetPluginMetadata(string pluginDirectory) { - string configPath = Path.Combine(pluginDirectory, PluginConfigName); + string configPath = Path.Combine(pluginDirectory, Constant.PluginMetadataFileName); if (!File.Exists(configPath)) { Log.Error($"|PluginConfig.GetPluginMetadata|Didn't find config file <{configPath}>"); @@ -81,7 +74,6 @@ private static PluginMetadata GetPluginMetadata(string pluginDirectory) return null; } - if (!AllowedLanguage.IsAllowed(metadata.Language)) { Log.Error($"|PluginConfig.GetPluginMetadata|Invalid language <{metadata.Language}> for config <{configPath}>"); diff --git a/Flow.Launcher.Core/Plugin/PluginInstaller.cs b/Flow.Launcher.Core/Plugin/PluginInstaller.cs index df38ca608cd..7b980a3eea0 100644 --- a/Flow.Launcher.Core/Plugin/PluginInstaller.cs +++ b/Flow.Launcher.Core/Plugin/PluginInstaller.cs @@ -1,9 +1,11 @@ -using System; +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 { @@ -13,28 +15,28 @@ internal static void Install(string path) { if (File.Exists(path)) { - string tempFoler = Path.Combine(Path.GetTempPath(), "flowlauncher\\plugins"); - if (Directory.Exists(tempFoler)) + string tempFolder = Path.Combine(Path.GetTempPath(), "flowlauncher", "plugins"); + if (Directory.Exists(tempFolder)) { - Directory.Delete(tempFoler, true); + Directory.Delete(tempFolder, true); } - UnZip(path, tempFoler, true); + UnZip(path, tempFolder, true); - string iniPath = Path.Combine(tempFoler, "plugin.json"); - if (!File.Exists(iniPath)) + string jsonPath = Path.Combine(tempFolder, Constant.PluginMetadataFileName); + if (!File.Exists(jsonPath)) { MessageBox.Show("Install failed: plugin config is missing"); return; } - PluginMetadata plugin = GetMetadataFromJson(tempFoler); + PluginMetadata plugin = GetMetadataFromJson(tempFolder); if (plugin == null || plugin.Name == null) { MessageBox.Show("Install failed: plugin config is invalid"); return; } - string pluginFolerPath = Infrastructure.UserSettings.DataLocation.PluginsDirectory; + string pluginFolderPath = Infrastructure.UserSettings.DataLocation.PluginsDirectory; string newPluginName = plugin.Name .Replace("/", "_") @@ -46,7 +48,9 @@ internal static void Install(string path) .Replace("*", "_") .Replace("|", "_") + "-" + Guid.NewGuid(); - string newPluginPath = Path.Combine(pluginFolerPath, newPluginName); + + 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}" + @@ -71,8 +75,7 @@ internal static void Install(string path) File.Create(Path.Combine(existingPlugin.Metadata.PluginDirectory, "NeedDelete.txt")).Close(); } - UnZip(path, newPluginPath, true); - Directory.Delete(tempFoler, true); + 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 @@ -94,7 +97,7 @@ internal static void Install(string path) private static PluginMetadata GetMetadataFromJson(string pluginDirectory) { - string configPath = Path.Combine(pluginDirectory, "plugin.json"); + string configPath = Path.Combine(pluginDirectory, Constant.PluginMetadataFileName); PluginMetadata metadata; if (!File.Exists(configPath)) @@ -107,36 +110,20 @@ private static PluginMetadata GetMetadataFromJson(string pluginDirectory) metadata = JsonConvert.DeserializeObject(File.ReadAllText(configPath)); metadata.PluginDirectory = pluginDirectory; } - catch (Exception) + catch (Exception e) { - string error = $"Parse plugin config {configPath} failed: json format is not valid"; -#if (DEBUG) - { - throw new Exception(error); - } -#endif + Log.Exception($"|PluginInstaller.GetMetadataFromJson|plugin config {configPath} failed: invalid json format", e); return null; } - if (!AllowedLanguage.IsAllowed(metadata.Language)) { - string error = $"Parse plugin config {configPath} failed: invalid language {metadata.Language}"; -#if (DEBUG) - { - throw new Exception(error); - } -#endif + Log.Error($"|PluginInstaller.GetMetadataFromJson|plugin config {configPath} failed: invalid language {metadata.Language}"); return null; } if (!File.Exists(metadata.ExecuteFilePath)) { - string error = $"Parse plugin config {configPath} failed: ExecuteFile {metadata.ExecuteFilePath} didn't exist"; -#if (DEBUG) - { - throw new Exception(error); - } -#endif + Log.Error($"|PluginInstaller.GetMetadataFromJson|plugin config {configPath} failed: file {metadata.ExecuteFilePath} doesn't exist"); return null; } @@ -144,58 +131,38 @@ private static PluginMetadata GetMetadataFromJson(string pluginDirectory) } /// - /// unzip + /// unzip plugin contents to the given directory. /// - /// The ziped file. - /// The STR directory. + /// The path to the zip file. + /// The output directory. /// overwirte - private static void UnZip(string zipedFile, string strDirectory, bool overWrite) + private static void UnZip(string zipFile, string strDirectory, bool overWrite) { if (strDirectory == "") strDirectory = Directory.GetCurrentDirectory(); - if (!strDirectory.EndsWith("\\")) - strDirectory = strDirectory + "\\"; - using (ZipInputStream s = new ZipInputStream(File.OpenRead(zipedFile))) + using (ZipInputStream zipStream = new ZipInputStream(File.OpenRead(zipFile))) { ZipEntry theEntry; - while ((theEntry = s.GetNextEntry()) != null) + while ((theEntry = zipStream.GetNextEntry()) != null) { - string directoryName = ""; - string pathToZip = ""; - pathToZip = theEntry.Name; + 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); - if (pathToZip != "") - directoryName = Path.GetDirectoryName(pathToZip) + "\\"; + Directory.CreateDirectory(destinationDir); - string fileName = Path.GetFileName(pathToZip); + if (String.IsNullOrEmpty(fileName) || (File.Exists(destinationFile) && !overWrite)) + continue; - Directory.CreateDirectory(strDirectory + directoryName); - - if (fileName != "") + using (FileStream streamWriter = File.Create(destinationFile)) { - if ((File.Exists(strDirectory + directoryName + fileName) && overWrite) || (!File.Exists(strDirectory + directoryName + fileName))) - { - using (FileStream streamWriter = File.Create(strDirectory + directoryName + fileName)) - { - byte[] data = new byte[2048]; - while (true) - { - int size = s.Read(data, 0, data.Length); - - if (size > 0) - streamWriter.Write(data, 0, size); - else - break; - } - streamWriter.Close(); - } - } + zipStream.CopyTo(streamWriter); } } - - s.Close(); } } } diff --git a/Flow.Launcher.Core/Plugin/PluginManager.cs b/Flow.Launcher.Core/Plugin/PluginManager.cs index b92226ccda5..5cde9de83a8 100644 --- a/Flow.Launcher.Core/Plugin/PluginManager.cs +++ b/Flow.Launcher.Core/Plugin/PluginManager.cs @@ -19,10 +19,6 @@ public static class PluginManager { private static IEnumerable _contextMenuPlugins; - /// - /// Directories that will hold Flow Launcher plugin directory - /// - public static List AllPlugins { get; private set; } public static readonly List GlobalPlugins = new List(); public static readonly Dictionary NonGlobalPlugins = new Dictionary(); @@ -32,27 +28,18 @@ public static class PluginManager // todo happlebao, this should not be public, the indicator function should be embeded public static PluginsSettings Settings; private static List _metadatas; - private static readonly string[] Directories = { Constant.PreinstalledDirectory, DataLocation.PluginsDirectory }; - private static void ValidateUserDirectory() - { - if (!Directory.Exists(DataLocation.PluginsDirectory)) - { - Directory.CreateDirectory(DataLocation.PluginsDirectory); - } - } + /// + /// Directories that will hold Flow Launcher plugin directory + /// + private static readonly string[] Directories = { Constant.PreinstalledDirectory, DataLocation.PluginsDirectory }; private static void DeletePythonBinding() { const string binding = "flowlauncher.py"; - var directory = DataLocation.PluginsDirectory; - foreach (var subDirectory in Directory.GetDirectories(directory)) + foreach (var subDirectory in Directory.GetDirectories(DataLocation.PluginsDirectory)) { - var path = Path.Combine(subDirectory, binding); - if (File.Exists(path)) - { - File.Delete(path); - } + File.Delete(Path.Combine(subDirectory, binding)); } } @@ -76,7 +63,8 @@ public static void ReloadData() static PluginManager() { - ValidateUserDirectory(); + // validate user directory + Directory.CreateDirectory(DataLocation.PluginsDirectory); // force old plugins use new python binding DeletePythonBinding(); } @@ -132,9 +120,10 @@ public static void InitializePlugins(IPublicAPI api) GlobalPlugins.Add(plugin); // Plugins may have multiple ActionKeywords, eg. WebSearch - plugin.Metadata.ActionKeywords.Where(x => x != Query.GlobalPluginWildcardSign) - .ToList() - .ForEach(x => NonGlobalPlugins[x] = plugin); + plugin.Metadata.ActionKeywords + .Where(x => x != Query.GlobalPluginWildcardSign) + .ToList() + .ForEach(x => NonGlobalPlugins[x] = plugin); } if (failedPlugins.Any()) @@ -164,9 +153,9 @@ public static List ValidPluginsForQuery(Query query) public static List QueryForPlugin(PluginPair pair, Query query) { + var results = new List(); try { - List results = null; var metadata = pair.Metadata; var milliseconds = Stopwatch.Debug($"|PluginManager.QueryForPlugin|Cost for {metadata.Name}", () => { @@ -175,13 +164,12 @@ public static List QueryForPlugin(PluginPair pair, Query query) }); metadata.QueryCount += 1; metadata.AvgQueryTime = metadata.QueryCount == 1 ? milliseconds : (metadata.AvgQueryTime + milliseconds) / 2; - return results; } catch (Exception e) { Log.Exception($"|PluginManager.QueryForPlugin|Exception for plugin <{pair.Metadata.Name}> when query <{query}>", e); - return new List(); } + return results; } public static void UpdatePluginMetadata(List results, PluginMetadata metadata, Query query) @@ -221,47 +209,34 @@ public static IEnumerable GetPluginsForInterface() where T : IFea public static List GetContextMenusForPlugin(Result result) { + var results = new List(); var pluginPair = _contextMenuPlugins.FirstOrDefault(o => o.Metadata.ID == result.PluginID); if (pluginPair != null) { - var metadata = pluginPair.Metadata; var plugin = (IContextMenu)pluginPair.Plugin; try { - var results = plugin.LoadContextMenus(result); + results = plugin.LoadContextMenus(result); foreach (var r in results) { - r.PluginDirectory = metadata.PluginDirectory; - r.PluginID = metadata.ID; + r.PluginDirectory = pluginPair.Metadata.PluginDirectory; + r.PluginID = pluginPair.Metadata.ID; r.OriginQuery = result.OriginQuery; } - return results; } catch (Exception e) { - Log.Exception($"|PluginManager.GetContextMenusForPlugin|Can't load context menus for plugin <{metadata.Name}>", e); - return new List(); + Log.Exception($"|PluginManager.GetContextMenusForPlugin|Can't load context menus for plugin <{pluginPair.Metadata.Name}>", e); } } - else - { - return new List(); - } - + return results; } public static bool ActionKeywordRegistered(string actionKeyword) { - if (actionKeyword != Query.GlobalPluginWildcardSign && - NonGlobalPlugins.ContainsKey(actionKeyword)) - { - return true; - } - else - { - return false; - } + return actionKeyword != Query.GlobalPluginWildcardSign + && NonGlobalPlugins.ContainsKey(actionKeyword); } /// @@ -299,7 +274,7 @@ public static void RemoveActionKeyword(string id, string oldActionkeyword) GlobalPlugins.Remove(plugin); } - if(oldActionkeyword != Query.GlobalPluginWildcardSign) + if (oldActionkeyword != Query.GlobalPluginWildcardSign) NonGlobalPlugins.Remove(oldActionkeyword); diff --git a/Flow.Launcher.Core/Plugin/PluginsLoader.cs b/Flow.Launcher.Core/Plugin/PluginsLoader.cs index 513d85c9670..1025f9bae34 100644 --- a/Flow.Launcher.Core/Plugin/PluginsLoader.cs +++ b/Flow.Launcher.Core/Plugin/PluginsLoader.cs @@ -21,7 +21,7 @@ public static class PluginsLoader public static List Plugins(List metadatas, PluginsSettings settings) { - var dotnetPlugins = DotNetPlugins(metadatas).ToList(); + var dotnetPlugins = DotNetPlugins(metadatas); var pythonPlugins = PythonPlugins(metadatas, settings.PythonDirectory); var executablePlugins = ExecutablePlugins(metadatas); var plugins = dotnetPlugins.Concat(pythonPlugins).Concat(executablePlugins).ToList(); @@ -46,75 +46,58 @@ public static IEnumerable DotNetPlugins(List source) var type = types.First(o => o.IsClass && !o.IsAbstract && o.GetInterfaces().Contains(typeof(IPlugin))); var plugin = (IPlugin)Activator.CreateInstance(type); #else - Assembly assembly; + Assembly assembly = null; + IPlugin plugin = null; + try { assembly = AssemblyLoadContext.Default.LoadFromAssemblyPath(metadata.ExecuteFilePath); - } - catch (Exception e) - { - erroredPlugins.Add(metadata.Name); - Log.Exception($"|PluginsLoader.DotNetPlugins|Couldn't load assembly for the plugin: {metadata.Name}", e); - return; - } + var types = assembly.GetTypes(); + var type = types.First(o => o.IsClass && !o.IsAbstract && o.GetInterfaces().Contains(typeof(IPlugin))); - Type type; - try + plugin = (IPlugin)Activator.CreateInstance(type); + } + catch (Exception e) when (assembly == null) { - var types = assembly.GetTypes(); - - type = types.First(o => o.IsClass && !o.IsAbstract && o.GetInterfaces().Contains(typeof(IPlugin))); + Log.Exception($"|PluginsLoader.DotNetPlugins|Couldn't load assembly for the plugin: {metadata.Name}", e); } catch (InvalidOperationException e) { - erroredPlugins.Add(metadata.Name); - Log.Exception($"|PluginsLoader.DotNetPlugins|Can't find the required IPlugin interface for the plugin: <{metadata.Name}>", e); - return; } catch (ReflectionTypeLoadException e) { - erroredPlugins.Add(metadata.Name); - Log.Exception($"|PluginsLoader.DotNetPlugins|The GetTypes method was unable to load assembly types for the plugin: <{metadata.Name}>", e); - return; } - - IPlugin plugin; - try + catch (Exception e) { - plugin = (IPlugin)Activator.CreateInstance(type); + Log.Exception($"|PluginsLoader.DotNetPlugins|The following plugin has errored and can not be loaded: <{metadata.Name}>", e); } - catch (Exception e) + + if (plugin == null) { erroredPlugins.Add(metadata.Name); - - Log.Exception($"|PluginsLoader.DotNetPlugins|The following plugin has errored and can not be loaded: <{metadata.Name}>", e); return; } #endif - PluginPair pair = new PluginPair + plugins.Add(new PluginPair { Plugin = plugin, Metadata = metadata - }; - plugins.Add(pair); + }); }); metadata.InitTime += milliseconds; - } if (erroredPlugins.Count > 0) { - var errorPluginString = ""; + var errorPluginString = String.Join(Environment.NewLine, erroredPlugins); var errorMessage = "The following " + (erroredPlugins.Count > 1 ? "plugins have " : "plugin has ") + "errored and cannot be loaded:"; - erroredPlugins.ForEach(x => errorPluginString += x + Environment.NewLine); - Task.Run(() => { MessageBox.Show($"{errorMessage}{Environment.NewLine}{Environment.NewLine}" + @@ -127,65 +110,74 @@ public static IEnumerable DotNetPlugins(List source) return plugins; } - public static IEnumerable PythonPlugins(List source, string pythonDirecotry) + public static IEnumerable PythonPlugins(List source, string pythonDirectory) { - var metadatas = source.Where(o => o.Language.ToUpper() == AllowedLanguage.Python); - string filename; - - if (string.IsNullOrEmpty(pythonDirecotry)) + // try to set Constant.PythonPath, either from + // PATH or from the given pythonDirectory + if (string.IsNullOrEmpty(pythonDirectory)) { var paths = Environment.GetEnvironmentVariable(PATH); if (paths != null) { - var pythonPaths = paths.Split(';').Where(p => p.ToLower().Contains(Python)); - if (pythonPaths.Any()) + var pythonInPath = paths + .Split(';') + .Where(p => p.ToLower().Contains(Python)) + .Any(); + + if (pythonInPath) { - filename = PythonExecutable; + Constant.PythonPath = PythonExecutable; } else { Log.Error("|PluginsLoader.PythonPlugins|Python can't be found in PATH."); - return new List(); } } else { Log.Error("|PluginsLoader.PythonPlugins|PATH environment variable is not set."); - return new List(); } } else { - var path = Path.Combine(pythonDirecotry, PythonExecutable); + var path = Path.Combine(pythonDirectory, PythonExecutable); if (File.Exists(path)) { - filename = path; + Constant.PythonPath = path; } else { - Log.Error("|PluginsLoader.PythonPlugins|Can't find python executable in (); + Log.Error($"|PluginsLoader.PythonPlugins|Can't find python executable in {path}"); } } - Constant.PythonPath = filename; - var plugins = metadatas.Select(metadata => new PluginPair + + // if we have a path to the python executable, + // load every python plugin pair. + if (String.IsNullOrEmpty(Constant.PythonPath)) { - Plugin = new PythonPlugin(filename), - Metadata = metadata - }); - return plugins; + return new List(); + } + else + { + return source + .Where(o => o.Language.ToUpper() == AllowedLanguage.Python) + .Select(metadata => new PluginPair + { + Plugin = new PythonPlugin(Constant.PythonPath), + Metadata = metadata + }); + } } public static IEnumerable ExecutablePlugins(IEnumerable source) { - var metadatas = source.Where(o => o.Language.ToUpper() == AllowedLanguage.Executable); - - var plugins = metadatas.Select(metadata => new PluginPair - { - Plugin = new ExecutablePlugin(metadata.ExecuteFilePath), - Metadata = metadata - }); - return plugins; + return source + .Where(o => o.Language.ToUpper() == AllowedLanguage.Executable) + .Select(metadata => new PluginPair + { + Plugin = new ExecutablePlugin(metadata.ExecuteFilePath), + Metadata = metadata + }); } } diff --git a/Flow.Launcher.Core/Updater.cs b/Flow.Launcher.Core/Updater.cs index 3c4a15d3033..3036055ba23 100644 --- a/Flow.Launcher.Core/Updater.cs +++ b/Flow.Launcher.Core/Updater.cs @@ -86,8 +86,8 @@ public async Task UpdateApp(bool silentIfLatestVersion = true) var targetDestination = updateManager.RootAppDirectory + $"\\app-{newReleaseVersion.ToString()}\\{DataLocation.PortableFolderName}"; FilesFolders.Copy(DataLocation.PortableDataPath, targetDestination); if (!FilesFolders.VerifyBothFolderFilesEqual(DataLocation.PortableDataPath, targetDestination)) - MessageBox.Show(string.Format("Flow Launcher was not able to move your user profile data to the new update version. Please manually" + - "move your profile data folder from {0} to {1}", DataLocation.PortableDataPath, targetDestination)); + MessageBox.Show("Flow Launcher was not able to move your user profile data to the new update version. Please manually " + + $"move your profile data folder from {DataLocation.PortableDataPath} to {targetDestination}"); } else { diff --git a/Flow.Launcher.Infrastructure/Constant.cs b/Flow.Launcher.Infrastructure/Constant.cs index 0936ce8c44b..a96d0e5b3cd 100644 --- a/Flow.Launcher.Infrastructure/Constant.cs +++ b/Flow.Launcher.Infrastructure/Constant.cs @@ -8,6 +8,7 @@ public static class Constant { public const string FlowLauncher = "Flow.Launcher"; public const string Plugins = "Plugins"; + public const string PluginMetadataFileName = "plugin.json"; public const string ApplicationFileName = FlowLauncher + ".exe"; diff --git a/Plugins/Flow.Launcher.Plugin.PluginManagement/Main.cs b/Plugins/Flow.Launcher.Plugin.PluginManagement/Main.cs index 586513acd6f..e1b631517e2 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginManagement/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.PluginManagement/Main.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.IO; using System.Linq; @@ -17,7 +17,6 @@ namespace Flow.Launcher.Plugin.PluginManagement public class Main : IPlugin, IPluginI18n { private static string APIBASE = "http://api.wox.one"; - private static string PluginConfigName = "plugin.json"; private static string pluginSearchUrl = APIBASE + "/plugin/search/"; private const string ListCommand = "list"; private const string InstallCommand = "install";