Skip to content

Commit d57f718

Browse files
authored
Merge branch 'dev' into binary_formatter
2 parents fd9e8a5 + ecd237d commit d57f718

File tree

19 files changed

+369
-175
lines changed

19 files changed

+369
-175
lines changed

Flow.Launcher.Core/Flow.Launcher.Core.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@
5454

5555
<ItemGroup>
5656
<PackageReference Include="Droplex" Version="1.7.0" />
57-
<PackageReference Include="FSharp.Core" Version="7.0.400" />
57+
<PackageReference Include="FSharp.Core" Version="7.0.401" />
5858
<PackageReference Include="Microsoft.IO.RecyclableMemoryStream" Version="2.3.2" />
5959
<PackageReference Include="squirrel.windows" Version="1.5.2" NoWarn="NU1701" />
6060
<PackageReference Include="StreamJsonRpc" Version="2.16.36" />

Flow.Launcher.Core/Plugin/JsonPRCModel.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ public record JsonRPCErrorModel(int Code, string Message, string Data);
2525
public record JsonRPCResponseModel(int Id, JsonRPCErrorModel Error = default) : JsonRPCBase(Id, Error);
2626
public record JsonRPCQueryResponseModel(int Id,
2727
[property: JsonPropertyName("result")] List<JsonRPCResult> Result,
28-
IReadOnlyDictionary<string, object> SettingsChanges = null,
28+
IReadOnlyDictionary<string, object> SettingsChange = null,
2929
string DebugMessage = "",
3030
JsonRPCErrorModel Error = default) : JsonRPCResponseModel(Id, Error);
3131

Flow.Launcher.Core/Plugin/JsonRPCPluginBase.cs

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ protected List<Result> ParseResults(JsonRPCQueryResponseModel queryResponseModel
9494

9595
results.AddRange(queryResponseModel.Result);
9696

97-
Settings?.UpdateSettings(queryResponseModel.SettingsChanges);
97+
Settings?.UpdateSettings(queryResponseModel.SettingsChange);
9898

9999
return results;
100100
}
@@ -126,14 +126,15 @@ protected void ExecuteFlowLauncherAPI(string method, object[] parameters)
126126

127127
private async Task InitSettingAsync()
128128
{
129-
if (!File.Exists(SettingConfigurationPath))
130-
return;
129+
JsonRpcConfigurationModel configuration = null;
130+
if (File.Exists(SettingConfigurationPath))
131+
{
132+
var deserializer = new DeserializerBuilder().WithNamingConvention(CamelCaseNamingConvention.Instance).Build();
133+
configuration =
134+
deserializer.Deserialize<JsonRpcConfigurationModel>(
135+
await File.ReadAllTextAsync(SettingConfigurationPath));
136+
}
131137

132-
var deserializer = new DeserializerBuilder().WithNamingConvention(CamelCaseNamingConvention.Instance)
133-
.Build();
134-
var configuration =
135-
deserializer.Deserialize<JsonRpcConfigurationModel>(
136-
await File.ReadAllTextAsync(SettingConfigurationPath));
137138

138139
Settings ??= new JsonRPCPluginSettings
139140
{

Flow.Launcher.Core/Plugin/JsonRPCPluginSettings.cs

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using System.Collections.Generic;
1+
using System.Collections.Concurrent;
2+
using System.Collections.Generic;
23
using System.Threading.Tasks;
34
using System.Windows;
45
using System.Windows.Controls;
@@ -10,16 +11,16 @@ namespace Flow.Launcher.Core.Plugin
1011
{
1112
public class JsonRPCPluginSettings
1213
{
13-
public required JsonRpcConfigurationModel Configuration { get; init; }
14+
public required JsonRpcConfigurationModel? Configuration { get; init; }
1415

1516
public required string SettingPath { get; init; }
1617
public Dictionary<string, FrameworkElement> SettingControls { get; } = new();
1718

1819
public IReadOnlyDictionary<string, object> Inner => Settings;
19-
protected Dictionary<string, object> Settings { get; set; }
20+
protected ConcurrentDictionary<string, object> Settings { get; set; }
2021
public required IPublicAPI API { get; init; }
2122

22-
private JsonStorage<Dictionary<string, object>> _storage;
23+
private JsonStorage<ConcurrentDictionary<string, object>> _storage;
2324

2425
// maybe move to resource?
2526
private static readonly Thickness settingControlMargin = new(0, 9, 18, 9);
@@ -33,9 +34,14 @@ public class JsonRPCPluginSettings
3334

3435
public async Task InitializeAsync()
3536
{
36-
_storage = new JsonStorage<Dictionary<string, object>>(SettingPath);
37+
_storage = new JsonStorage<ConcurrentDictionary<string, object>>(SettingPath);
3738
Settings = await _storage.LoadAsync();
3839

40+
if (Settings != null)
41+
{
42+
return;
43+
}
44+
3945
foreach (var (type, attributes) in Configuration.Body)
4046
{
4147
if (attributes.Name == null)
@@ -58,10 +64,7 @@ public void UpdateSettings(IReadOnlyDictionary<string, object> settings)
5864

5965
foreach (var (key, value) in settings)
6066
{
61-
if (Settings.ContainsKey(key))
62-
{
63-
Settings[key] = value;
64-
}
67+
Settings[key] = value;
6568

6669
if (SettingControls.TryGetValue(key, out var control))
6770
{
@@ -82,6 +85,7 @@ public void UpdateSettings(IReadOnlyDictionary<string, object> settings)
8285
}
8386
}
8487
}
88+
Save();
8589
}
8690

8791
public async Task SaveAsync()

Flow.Launcher.Core/Plugin/JsonRPCPluginV2.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ async Task ReadErrorAsync()
9191

9292
private void SetupJsonRPC()
9393
{
94-
var formatter = new SystemTextJsonFormatter();
94+
var formatter = new JsonMessageFormatter();
9595
var handler = new NewLineDelimitedMessageHandler(ClientPipe,
9696
formatter);
9797

Flow.Launcher.Core/Plugin/PluginManager.cs

Lines changed: 155 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
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 System.Text.Json;
1416

1517
namespace Flow.Launcher.Core.Plugin
1618
{
@@ -27,9 +29,9 @@ public static class PluginManager
2729

2830
public static IPublicAPI API { private set; get; }
2931

30-
// todo happlebao, this should not be public, the indicator function should be embeded
31-
public static PluginsSettings Settings;
32+
private static PluginsSettings Settings;
3233
private static List<PluginMetadata> _metadatas;
34+
private static List<string> _modifiedPlugins = new List<string>();
3335

3436
/// <summary>
3537
/// Directories that will hold Flow Launcher plugin directory
@@ -331,5 +333,156 @@ public static void ReplaceActionKeyword(string id, string oldActionKeyword, stri
331333
RemoveActionKeyword(id, oldActionKeyword);
332334
}
333335
}
336+
337+
private static string GetContainingFolderPathAfterUnzip(string unzippedParentFolderPath)
338+
{
339+
var unzippedFolderCount = Directory.GetDirectories(unzippedParentFolderPath).Length;
340+
var unzippedFilesCount = Directory.GetFiles(unzippedParentFolderPath).Length;
341+
342+
// adjust path depending on how the plugin is zipped up
343+
// the recommended should be to zip up the folder not the contents
344+
if (unzippedFolderCount == 1 && unzippedFilesCount == 0)
345+
// folder is zipped up, unzipped plugin directory structure: tempPath/unzippedParentPluginFolder/pluginFolderName/
346+
return Directory.GetDirectories(unzippedParentFolderPath)[0];
347+
348+
if (unzippedFilesCount > 1)
349+
// content is zipped up, unzipped plugin directory structure: tempPath/unzippedParentPluginFolder/
350+
return unzippedParentFolderPath;
351+
352+
return string.Empty;
353+
}
354+
355+
private static bool SameOrLesserPluginVersionExists(string metadataPath)
356+
{
357+
var newMetadata = JsonSerializer.Deserialize<PluginMetadata>(File.ReadAllText(metadataPath));
358+
return AllPlugins.Any(x => x.Metadata.ID == newMetadata.ID
359+
&& newMetadata.Version.CompareTo(x.Metadata.Version) <= 0);
360+
}
361+
362+
#region Public functions
363+
364+
public static bool PluginModified(string uuid)
365+
{
366+
return _modifiedPlugins.Contains(uuid);
367+
}
368+
369+
370+
/// <summary>
371+
/// Update a plugin to new version, from a zip file. Will Delete zip after updating.
372+
/// </summary>
373+
public static void UpdatePlugin(PluginMetadata existingVersion, UserPlugin newVersion, string zipFilePath)
374+
{
375+
InstallPlugin(newVersion, zipFilePath, checkModified:false);
376+
UninstallPlugin(existingVersion, removeSettings:false, checkModified:false);
377+
_modifiedPlugins.Add(existingVersion.ID);
378+
}
379+
380+
/// <summary>
381+
/// Install a plugin. Will Delete zip after updating.
382+
/// </summary>
383+
public static void InstallPlugin(UserPlugin plugin, string zipFilePath)
384+
{
385+
InstallPlugin(plugin, zipFilePath, true);
386+
}
387+
388+
/// <summary>
389+
/// Uninstall a plugin.
390+
/// </summary>
391+
public static void UninstallPlugin(PluginMetadata plugin, bool removeSettings = true)
392+
{
393+
UninstallPlugin(plugin, removeSettings, true);
394+
}
395+
396+
#endregion
397+
398+
#region Internal functions
399+
400+
internal static void InstallPlugin(UserPlugin plugin, string zipFilePath, bool checkModified)
401+
{
402+
if (checkModified && PluginModified(plugin.ID))
403+
{
404+
// Distinguish exception from installing same or less version
405+
throw new ArgumentException($"Plugin {plugin.Name} {plugin.ID} has been modified.", nameof(plugin));
406+
}
407+
408+
// Unzip plugin files to temp folder
409+
var tempFolderPluginPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
410+
System.IO.Compression.ZipFile.ExtractToDirectory(zipFilePath, tempFolderPluginPath);
411+
File.Delete(zipFilePath);
412+
413+
var pluginFolderPath = GetContainingFolderPathAfterUnzip(tempFolderPluginPath);
414+
415+
var metadataJsonFilePath = string.Empty;
416+
if (File.Exists(Path.Combine(pluginFolderPath, Constant.PluginMetadataFileName)))
417+
metadataJsonFilePath = Path.Combine(pluginFolderPath, Constant.PluginMetadataFileName);
418+
419+
if (string.IsNullOrEmpty(metadataJsonFilePath) || string.IsNullOrEmpty(pluginFolderPath))
420+
{
421+
throw new FileNotFoundException($"Unable to find plugin.json from the extracted zip file, or this path {pluginFolderPath} does not exist");
422+
}
423+
424+
if (SameOrLesserPluginVersionExists(metadataJsonFilePath))
425+
{
426+
throw new InvalidOperationException($"A plugin with the same ID and version already exists, or the version is greater than this downloaded plugin {plugin.Name}");
427+
}
428+
429+
var folderName = string.IsNullOrEmpty(plugin.Version) ? $"{plugin.Name}-{Guid.NewGuid()}" : $"{plugin.Name}-{plugin.Version}";
430+
431+
var defaultPluginIDs = new List<string>
432+
{
433+
"0ECADE17459B49F587BF81DC3A125110", // BrowserBookmark
434+
"CEA0FDFC6D3B4085823D60DC76F28855", // Calculator
435+
"572be03c74c642baae319fc283e561a8", // Explorer
436+
"6A122269676E40EB86EB543B945932B9", // PluginIndicator
437+
"9f8f9b14-2518-4907-b211-35ab6290dee7", // PluginsManager
438+
"b64d0a79-329a-48b0-b53f-d658318a1bf6", // ProcessKiller
439+
"791FC278BA414111B8D1886DFE447410", // Program
440+
"D409510CD0D2481F853690A07E6DC426", // Shell
441+
"CEA08895D2544B019B2E9C5009600DF4", // Sys
442+
"0308FD86DE0A4DEE8D62B9B535370992", // URL
443+
"565B73353DBF4806919830B9202EE3BF", // WebSearch
444+
"5043CETYU6A748679OPA02D27D99677A" // WindowsSettings
445+
};
446+
447+
// Treat default plugin differently, it needs to be removable along with each flow release
448+
var installDirectory = !defaultPluginIDs.Any(x => x == plugin.ID)
449+
? DataLocation.PluginsDirectory
450+
: Constant.PreinstalledDirectory;
451+
452+
var newPluginPath = Path.Combine(installDirectory, folderName);
453+
454+
FilesFolders.CopyAll(pluginFolderPath, newPluginPath);
455+
456+
Directory.Delete(tempFolderPluginPath, true);
457+
458+
if (checkModified)
459+
{
460+
_modifiedPlugins.Add(plugin.ID);
461+
}
462+
}
463+
464+
internal static void UninstallPlugin(PluginMetadata plugin, bool removeSettings, bool checkModified)
465+
{
466+
if (checkModified && PluginModified(plugin.ID))
467+
{
468+
throw new ArgumentException($"Plugin {plugin.Name} has been modified");
469+
}
470+
471+
if (removeSettings)
472+
{
473+
Settings.Plugins.Remove(plugin.ID);
474+
AllPlugins.RemoveAll(p => p.Metadata.ID == plugin.ID);
475+
}
476+
477+
// Marked for deletion. Will be deleted on next start up
478+
using var _ = File.CreateText(Path.Combine(plugin.PluginDirectory, "NeedDelete.txt"));
479+
480+
if (checkModified)
481+
{
482+
_modifiedPlugins.Add(plugin.ID);
483+
}
484+
}
485+
486+
#endregion
334487
}
335488
}

Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@
6767
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
6868
</PackageReference>
6969
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.1" PrivateAssets="All" />
70-
<PackageReference Include="JetBrains.Annotations" Version="2023.2.0" />
70+
<PackageReference Include="JetBrains.Annotations" Version="2023.3.0" />
7171
<PackageReference Include="PropertyChanged.Fody" Version="3.4.0" />
7272
</ItemGroup>
7373

Flow.Launcher.Test/Flow.Launcher.Test.csproj

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,12 +49,12 @@
4949

5050
<ItemGroup>
5151
<PackageReference Include="Moq" Version="4.18.4" />
52-
<PackageReference Include="nunit" Version="3.13.3" />
52+
<PackageReference Include="nunit" Version="3.14.0" />
5353
<PackageReference Include="NUnit3TestAdapter" Version="4.5.0">
5454
<PrivateAssets>all</PrivateAssets>
5555
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
5656
</PackageReference>
57-
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.2" />
57+
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
5858
</ItemGroup>
5959

6060
</Project>

Flow.Launcher/Flow.Launcher.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@
9696
<PackageReference Include="NHotkey.Wpf" Version="2.1.1" />
9797
<PackageReference Include="PropertyChanged.Fody" Version="3.4.0" />
9898
<PackageReference Include="SharpVectors" Version="1.8.2" />
99-
<PackageReference Include="VirtualizingWrapPanel" Version="1.5.7" />
99+
<PackageReference Include="VirtualizingWrapPanel" Version="1.5.8" />
100100
</ItemGroup>
101101

102102
<ItemGroup>

Plugins/Flow.Launcher.Plugin.BrowserBookmark/Flow.Launcher.Plugin.BrowserBookmark.csproj

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<Project Sdk="Microsoft.NET.Sdk">
1+
<Project Sdk="Microsoft.NET.Sdk">
22

33
<PropertyGroup>
44
<OutputType>Library</OutputType>
@@ -56,7 +56,7 @@
5656
</ItemGroup>
5757

5858
<ItemGroup>
59-
<PackageReference Include="Microsoft.Data.Sqlite" Version="7.0.10" />
59+
<PackageReference Include="Microsoft.Data.Sqlite" Version="8.0.0" />
6060
</ItemGroup>
6161

6262
</Project>

0 commit comments

Comments
 (0)