Skip to content

Commit 8632d1d

Browse files
authored
Merge branch 'dev' into 240516GarulfQueryCycle
2 parents 3d6d79b + 1bbe030 commit 8632d1d

File tree

8 files changed

+156
-35
lines changed

8 files changed

+156
-35
lines changed

Flow.Launcher.Core/ExternalPlugins/UserPlugin.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
using System;
1+
using System;
22

33
namespace Flow.Launcher.Core.ExternalPlugins
44
{
@@ -13,9 +13,11 @@ public record UserPlugin
1313
public string Website { get; set; }
1414
public string UrlDownload { get; set; }
1515
public string UrlSourceCode { get; set; }
16+
public string LocalInstallPath { get; set; }
1617
public string IcoPath { get; set; }
1718
public DateTime? LatestReleaseDate { get; set; }
1819
public DateTime? DateAdded { get; set; }
1920

21+
public bool IsFromLocalInstallPath => !string.IsNullOrEmpty(LocalInstallPath);
2022
}
2123
}

Flow.Launcher.Core/Plugin/PluginManager.cs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -380,7 +380,8 @@ public static bool PluginModified(string uuid)
380380

381381

382382
/// <summary>
383-
/// Update a plugin to new version, from a zip file. Will Delete zip after updating.
383+
/// Update a plugin to new version, from a zip file. By default will remove the zip file if update is via url,
384+
/// unless it's a local path installation
384385
/// </summary>
385386
public static void UpdatePlugin(PluginMetadata existingVersion, UserPlugin newVersion, string zipFilePath)
386387
{
@@ -390,11 +391,11 @@ public static void UpdatePlugin(PluginMetadata existingVersion, UserPlugin newVe
390391
}
391392

392393
/// <summary>
393-
/// Install a plugin. Will Delete zip after updating.
394+
/// Install a plugin. By default will remove the zip file if installation is from url, unless it's a local path installation
394395
/// </summary>
395396
public static void InstallPlugin(UserPlugin plugin, string zipFilePath)
396397
{
397-
InstallPlugin(plugin, zipFilePath, true);
398+
InstallPlugin(plugin, zipFilePath, checkModified: true);
398399
}
399400

400401
/// <summary>
@@ -420,7 +421,9 @@ internal static void InstallPlugin(UserPlugin plugin, string zipFilePath, bool c
420421
// Unzip plugin files to temp folder
421422
var tempFolderPluginPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
422423
System.IO.Compression.ZipFile.ExtractToDirectory(zipFilePath, tempFolderPluginPath);
423-
File.Delete(zipFilePath);
424+
425+
if(!plugin.IsFromLocalInstallPath)
426+
File.Delete(zipFilePath);
424427

425428
var pluginFolderPath = GetContainingFolderPathAfterUnzip(tempFolderPluginPath);
426429

Flow.Launcher.Plugin/SharedCommands/FilesFolders.cs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using System;
22
using System.Diagnostics;
33
using System.IO;
4+
using System.Linq;
45
#pragma warning disable IDE0005
56
using System.Windows;
67
#pragma warning restore IDE0005
@@ -200,6 +201,24 @@ public static void OpenFile(string filePath, string workingDir = "", bool asAdmi
200201
}
201202
}
202203

204+
///<summary>
205+
/// This checks whether a given string is a zip file path.
206+
/// By default does not check if the zip file actually exist on disk, can do so by
207+
/// setting checkFileExists = true.
208+
///</summary>
209+
public static bool IsZipFilePath(string querySearchString, bool checkFileExists = false)
210+
{
211+
if (IsLocationPathString(querySearchString) && querySearchString.Split('.').Last() == "zip")
212+
{
213+
if (checkFileExists)
214+
return FileExists(querySearchString);
215+
216+
return true;
217+
}
218+
219+
return false;
220+
}
221+
203222
///<summary>
204223
/// This checks whether a given string is a directory path or network location string.
205224
/// It does not check if location actually exists.

Flow.Launcher.Test/Plugins/ExplorerTest.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,11 +191,15 @@ public void GivenQuery_WhenActionKeywordForFileContentSearchExists_ThenFileConte
191191
[TestCase(@"\c:\", false)]
192192
[TestCase(@"cc:\", false)]
193193
[TestCase(@"\\\SomeNetworkLocation\", false)]
194+
[TestCase(@"\\SomeNetworkLocation\", true)]
194195
[TestCase("RandomFile", false)]
195196
[TestCase(@"c:\>*", true)]
196197
[TestCase(@"c:\>", true)]
197198
[TestCase(@"c:\SomeLocation\SomeOtherLocation\>", true)]
198199
[TestCase(@"c:\SomeLocation\SomeOtherLocation", true)]
200+
[TestCase(@"c:\SomeLocation\SomeOtherLocation\SomeFile.exe", true)]
201+
[TestCase(@"\\SomeNetworkLocation\SomeFile.exe", true)]
202+
199203
public void WhenGivenQuerySearchString_ThenShouldIndicateIfIsLocationPathString(string querySearchString, bool expectedResult)
200204
{
201205
// When, Given
2.87 KB
Loading

Plugins/Flow.Launcher.Plugin.PluginsManager/Languages/en.xaml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@
2626
<system:String x:Key="plugin_pluginsmanager_update_prompt">{0} by {1} {2}{3}Would you like to update this plugin? After the update Flow will automatically restart.</system:String>
2727
<system:String x:Key="plugin_pluginsmanager_update_prompt_no_restart">{0} by {1} {2}{2}Would you like to update this plugin?</system:String>
2828
<system:String x:Key="plugin_pluginsmanager_update_title">Plugin Update</system:String>
29-
<system:String x:Key="plugin_pluginsmanager_update_exists">This plugin has an update, would you like to see it?</system:String>
3029
<system:String x:Key="plugin_pluginsmanager_update_alreadyexists">This plugin is already installed</system:String>
3130
<system:String x:Key="plugin_pluginsmanager_update_failed_title">Plugin Manifest Download Failed</system:String>
3231
<system:String x:Key="plugin_pluginsmanager_update_failed_subtitle">Please check if you can connect to github.com. This error means you may not be able to install or update plugins.</system:String>

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

Lines changed: 94 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,9 @@
33
using Flow.Launcher.Infrastructure;
44
using Flow.Launcher.Infrastructure.Http;
55
using Flow.Launcher.Infrastructure.Logger;
6-
using Flow.Launcher.Infrastructure.UserSettings;
76
using Flow.Launcher.Plugin.SharedCommands;
87
using System;
98
using System.Collections.Generic;
10-
using System.Diagnostics;
119
using System.IO;
1210
using System.Linq;
1311
using System.Net.Http;
@@ -99,13 +97,12 @@ internal async Task InstallOrUpdateAsync(UserPlugin plugin)
9997
if (Context.API.GetAllPlugins()
10098
.Any(x => x.Metadata.ID == plugin.ID && x.Metadata.Version.CompareTo(plugin.Version) < 0))
10199
{
102-
if (MessageBox.Show(Context.API.GetTranslation("plugin_pluginsmanager_update_exists"),
103-
Context.API.GetTranslation("plugin_pluginsmanager_update_title"),
104-
MessageBoxButton.YesNo) == MessageBoxResult.Yes)
105-
Context
106-
.API
107-
.ChangeQuery(
108-
$"{Context.CurrentPluginMetadata.ActionKeywords.FirstOrDefault()} {Settings.UpdateCommand} {plugin.Name}");
100+
var updateDetail = !plugin.IsFromLocalInstallPath ? plugin.Name : plugin.LocalInstallPath;
101+
102+
Context
103+
.API
104+
.ChangeQuery(
105+
$"{Context.CurrentPluginMetadata.ActionKeywords.FirstOrDefault()} {Settings.UpdateCommand} {updateDetail}");
109106

110107
var mainWindow = Application.Current.MainWindow;
111108
mainWindow.Show();
@@ -147,12 +144,17 @@ internal async Task InstallOrUpdateAsync(UserPlugin plugin)
147144

148145
try
149146
{
150-
if (File.Exists(filePath))
147+
if (!plugin.IsFromLocalInstallPath)
151148
{
152-
File.Delete(filePath);
153-
}
149+
if (File.Exists(filePath))
150+
File.Delete(filePath);
154151

155-
await Http.DownloadAsync(plugin.UrlDownload, filePath).ConfigureAwait(false);
152+
await Http.DownloadAsync(plugin.UrlDownload, filePath).ConfigureAwait(false);
153+
}
154+
else
155+
{
156+
filePath = plugin.LocalInstallPath;
157+
}
156158

157159
Install(plugin, filePath);
158160
}
@@ -193,24 +195,38 @@ internal async ValueTask<List<Result>> RequestUpdateAsync(string search, Cancell
193195
{
194196
await PluginsManifest.UpdateManifestAsync(token, usePrimaryUrlOnly);
195197

198+
var pluginFromLocalPath = null as UserPlugin;
199+
var updateFromLocalPath = false;
200+
201+
if (FilesFolders.IsZipFilePath(search, checkFileExists: true))
202+
{
203+
pluginFromLocalPath = Utilities.GetPluginInfoFromZip(search);
204+
pluginFromLocalPath.LocalInstallPath = search;
205+
updateFromLocalPath = true;
206+
}
207+
208+
var updateSource = !updateFromLocalPath
209+
? PluginsManifest.UserPlugins
210+
: new List<UserPlugin> { pluginFromLocalPath };
211+
196212
var resultsForUpdate = (
197213
from existingPlugin in Context.API.GetAllPlugins()
198-
join pluginFromManifest in PluginsManifest.UserPlugins
199-
on existingPlugin.Metadata.ID equals pluginFromManifest.ID
200-
where String.Compare(existingPlugin.Metadata.Version, pluginFromManifest.Version,
214+
join pluginUpdateSource in updateSource
215+
on existingPlugin.Metadata.ID equals pluginUpdateSource.ID
216+
where string.Compare(existingPlugin.Metadata.Version, pluginUpdateSource.Version,
201217
StringComparison.InvariantCulture) <
202-
0 // if current version precedes manifest version
218+
0 // if current version precedes version of the plugin from update source (e.g. PluginsManifest)
203219
&& !PluginManager.PluginModified(existingPlugin.Metadata.ID)
204220
select
205221
new
206222
{
207-
pluginFromManifest.Name,
208-
pluginFromManifest.Author,
223+
pluginUpdateSource.Name,
224+
pluginUpdateSource.Author,
209225
CurrentVersion = existingPlugin.Metadata.Version,
210-
NewVersion = pluginFromManifest.Version,
226+
NewVersion = pluginUpdateSource.Version,
211227
existingPlugin.Metadata.IcoPath,
212228
PluginExistingMetadata = existingPlugin.Metadata,
213-
PluginNewUserPlugin = pluginFromManifest
229+
PluginNewUserPlugin = pluginUpdateSource
214230
}).ToList();
215231

216232
if (!resultsForUpdate.Any())
@@ -261,13 +277,21 @@ where String.Compare(existingPlugin.Metadata.Version, pluginFromManifest.Version
261277

262278
_ = Task.Run(async delegate
263279
{
264-
if (File.Exists(downloadToFilePath))
280+
if (!x.PluginNewUserPlugin.IsFromLocalInstallPath)
265281
{
266-
File.Delete(downloadToFilePath);
267-
}
282+
if (File.Exists(downloadToFilePath))
283+
{
284+
File.Delete(downloadToFilePath);
285+
}
268286

269-
await Http.DownloadAsync(x.PluginNewUserPlugin.UrlDownload, downloadToFilePath)
270-
.ConfigureAwait(false);
287+
await Http.DownloadAsync(x.PluginNewUserPlugin.UrlDownload, downloadToFilePath)
288+
.ConfigureAwait(false);
289+
}
290+
else
291+
{
292+
downloadToFilePath = x.PluginNewUserPlugin.LocalInstallPath;
293+
}
294+
271295

272296
PluginManager.UpdatePlugin(x.PluginExistingMetadata, x.PluginNewUserPlugin,
273297
downloadToFilePath);
@@ -396,7 +420,7 @@ await Http.DownloadAsync(plugin.PluginNewUserPlugin.UrlDownload, downloadToFileP
396420
results = results.Prepend(updateAllResult);
397421
}
398422

399-
return Search(results, search);
423+
return !updateFromLocalPath ? Search(results, search) : results.ToList();
400424
}
401425

402426
internal bool PluginExists(string id)
@@ -470,6 +494,42 @@ internal List<Result> InstallFromWeb(string url)
470494
return new List<Result> { result };
471495
}
472496

497+
internal List<Result> InstallFromLocalPath(string localPath)
498+
{
499+
var plugin = Utilities.GetPluginInfoFromZip(localPath);
500+
501+
plugin.LocalInstallPath = localPath;
502+
503+
return new List<Result>
504+
{
505+
new Result
506+
{
507+
Title = $"{plugin.Name} by {plugin.Author}",
508+
SubTitle = plugin.Description,
509+
IcoPath = plugin.IcoPath,
510+
Action = e =>
511+
{
512+
if (Settings.WarnFromUnknownSource)
513+
{
514+
if (!InstallSourceKnown(plugin.Website)
515+
&& MessageBox.Show(string.Format(
516+
Context.API.GetTranslation("plugin_pluginsmanager_install_unknown_source_warning"),
517+
Environment.NewLine),
518+
Context.API.GetTranslation(
519+
"plugin_pluginsmanager_install_unknown_source_warning_title"),
520+
MessageBoxButton.YesNo) == MessageBoxResult.No)
521+
return false;
522+
}
523+
524+
Application.Current.MainWindow.Hide();
525+
_ = InstallOrUpdateAsync(plugin);
526+
527+
return ShouldHideWindow;
528+
}
529+
}
530+
};
531+
}
532+
473533
private bool InstallSourceKnown(string url)
474534
{
475535
var author = url.Split('/')[3];
@@ -489,6 +549,9 @@ internal async ValueTask<List<Result>> RequestInstallOrUpdate(string search, Can
489549
&& search.Split('.').Last() == zip)
490550
return InstallFromWeb(search);
491551

552+
if (FilesFolders.IsZipFilePath(search, checkFileExists: true))
553+
return InstallFromLocalPath(search);
554+
492555
var results =
493556
PluginsManifest
494557
.UserPlugins
@@ -522,10 +585,13 @@ private void Install(UserPlugin plugin, string downloadedFilePath)
522585
if (!File.Exists(downloadedFilePath))
523586
throw new FileNotFoundException($"Plugin {plugin.ID} zip file not found at {downloadedFilePath}",
524587
downloadedFilePath);
588+
525589
try
526590
{
527591
PluginManager.InstallPlugin(plugin, downloadedFilePath);
528-
File.Delete(downloadedFilePath);
592+
593+
if (!plugin.IsFromLocalInstallPath)
594+
File.Delete(downloadedFilePath);
529595
}
530596
catch (FileNotFoundException e)
531597
{

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

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
1-
using ICSharpCode.SharpZipLib.Zip;
1+
using Flow.Launcher.Core.ExternalPlugins;
2+
using Flow.Launcher.Infrastructure.UserSettings;
3+
using ICSharpCode.SharpZipLib.Zip;
4+
using Newtonsoft.Json;
25
using System.IO;
6+
using System.IO.Compression;
7+
using System.Linq;
38

49
namespace Flow.Launcher.Plugin.PluginsManager
510
{
@@ -55,5 +60,28 @@ internal static string GetContainingFolderPathAfterUnzip(string unzippedParentFo
5560

5661
return string.Empty;
5762
}
63+
64+
internal static UserPlugin GetPluginInfoFromZip(string filePath)
65+
{
66+
var plugin = null as UserPlugin;
67+
68+
using (ZipArchive archive = System.IO.Compression.ZipFile.OpenRead(filePath))
69+
{
70+
var pluginJsonPath = archive.Entries.FirstOrDefault(x => x.Name == "plugin.json").ToString();
71+
ZipArchiveEntry pluginJsonEntry = archive.GetEntry(pluginJsonPath);
72+
73+
if (pluginJsonEntry != null)
74+
{
75+
using (StreamReader reader = new StreamReader(pluginJsonEntry.Open()))
76+
{
77+
string pluginJsonContent = reader.ReadToEnd();
78+
plugin = JsonConvert.DeserializeObject<UserPlugin>(pluginJsonContent);
79+
plugin.IcoPath = "Images\\zipfolder.png";
80+
}
81+
}
82+
}
83+
84+
return plugin;
85+
}
5886
}
5987
}

0 commit comments

Comments
 (0)