Skip to content

Commit 248b098

Browse files
committed
Support installing from local path
1 parent f9e7b82 commit 248b098

File tree

4 files changed

+102
-1
lines changed

4 files changed

+102
-1
lines changed

Flow.Launcher.Core/Plugin/PluginManager.cs

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using System.Collections.Concurrent;
33
using System.Collections.Generic;
44
using System.IO;
5+
using System.IO.Compression;
56
using System.Linq;
67
using System.Text.Json;
78
using System.Threading;
@@ -633,6 +634,42 @@ await DownloadFileAsync(
633634
}
634635
}
635636

637+
public static async Task InstallPluginAndCheckRestartAsync(string filePath)
638+
{
639+
UserPlugin plugin;
640+
try
641+
{
642+
using ZipArchive archive = ZipFile.OpenRead(filePath);
643+
var pluginJsonPath = archive.Entries.FirstOrDefault(x => x.Name == "plugin.json") ??
644+
throw new FileNotFoundException("The zip file does not contain a plugin.json file.");
645+
var pluginJsonEntry = archive.GetEntry(pluginJsonPath.ToString()) ??
646+
throw new FileNotFoundException("The zip file does not contain a plugin.json file.");
647+
648+
using Stream stream = pluginJsonEntry.Open();
649+
plugin = JsonSerializer.Deserialize<UserPlugin>(stream);
650+
plugin.IcoPath = "Images\\zipfolder.png";
651+
plugin.LocalInstallPath = filePath;
652+
}
653+
catch (Exception e)
654+
{
655+
API.LogException(ClassName, "Failed to validate zip file", e);
656+
API.ShowMsgError(API.GetTranslation("ZipFileNotHavePluginJson"));
657+
return;
658+
}
659+
660+
if (FlowSettings.ShowUnknownSourceWarning)
661+
{
662+
if (!InstallSourceKnown(plugin.Website)
663+
&& API.ShowMsgBox(string.Format(
664+
API.GetTranslation("InstallFromUnknownSourceSubtitle"), Environment.NewLine),
665+
API.GetTranslation("InstallFromUnknownSourceTitle"),
666+
MessageBoxButton.YesNo) == MessageBoxResult.No)
667+
return;
668+
}
669+
670+
await InstallPluginAndCheckRestartAsync(plugin);
671+
}
672+
636673
public static async Task UninstallPluginAndCheckRestartAsync(PluginMetadata oldPlugin)
637674
{
638675
if (API.ShowMsgBox(
@@ -913,6 +950,24 @@ await API.ShowProgressBoxAsync(prgBoxTitle,
913950
}
914951
}
915952

953+
private static bool InstallSourceKnown(string url)
954+
{
955+
var pieces = url.Split('/');
956+
957+
if (pieces.Length < 4)
958+
return false;
959+
960+
var author = pieces[3];
961+
var acceptedSource = "https://github.com";
962+
var constructedUrlPart = string.Format("{0}/{1}/", acceptedSource, author);
963+
964+
return url.StartsWith(acceptedSource) &&
965+
API.GetAllPlugins().Any(x =>
966+
!string.IsNullOrEmpty(x.Metadata.Website) &&
967+
x.Metadata.Website.StartsWith(constructedUrlPart)
968+
);
969+
}
970+
916971
#endregion
917972
}
918973
}

Flow.Launcher/Languages/en.xaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,12 @@
204204
<system:String x:Key="UpdatePromptSubtitle">{0} by {1} {2}{2}Would you like to update this plugin?</system:String>
205205
<system:String x:Key="DownloadingPlugin">Downloading plugin</system:String>
206206
<system:String x:Key="AutoRestartAfterChange">Automatically restart after installing/uninstalling/updating plugins in plugin store</system:String>
207+
<system:String x:Key="ZipFileNotHavePluginJson">Zip file does not have a valid plugin.json configuration</system:String>
208+
<system:String x:Key="InstallFromUnknownSourceTitle">Installing from an unknown source</system:String>
209+
<system:String x:Key="InstallFromUnknownSourceSubtitle">This plugin is 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 in general section of setting window)</system:String>
210+
<system:String x:Key="ZipFiles">Zip files</system:String>
211+
<system:String x:Key="SelectZipFile">Please select zip file</system:String>
212+
<system:String x:Key="installLocalPluginTooltip">Install plugin from local path</system:String>
207213

208214
<!-- Setting Theme -->
209215
<system:String x:Key="theme">Theme</system:String>

Flow.Launcher/SettingPages/ViewModels/SettingsPanePluginStoreViewModel.cs

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
1-
using System.Collections.Generic;
1+
using System;
2+
using System.Collections.Generic;
23
using System.Linq;
34
using System.Threading.Tasks;
5+
using System.Windows.Forms;
46
using CommunityToolkit.Mvvm.Input;
7+
using Flow.Launcher.Core.Plugin;
58
using Flow.Launcher.Plugin;
69
using Flow.Launcher.ViewModel;
710

@@ -96,6 +99,36 @@ private async Task RefreshExternalPluginsAsync()
9699
}
97100
}
98101

102+
[RelayCommand]
103+
private async Task InstallPluginAsync()
104+
{
105+
var file = GetFileFromDialog(
106+
App.API.GetTranslation("SelectZipFile"),
107+
$"{App.API.GetTranslation("ZipFiles")} (*.zip)|*.zip");
108+
109+
if (!string.IsNullOrEmpty(file))
110+
await PluginManager.InstallPluginAndCheckRestartAsync(file);
111+
}
112+
113+
private static string GetFileFromDialog(string title, string filter = "")
114+
{
115+
var dlg = new OpenFileDialog
116+
{
117+
InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile) + "\\Downloads",
118+
Multiselect = false,
119+
CheckFileExists = true,
120+
CheckPathExists = true,
121+
Title = title,
122+
Filter = filter
123+
};
124+
125+
return dlg.ShowDialog() switch
126+
{
127+
DialogResult.OK => dlg.FileName,
128+
_ => string.Empty
129+
};
130+
}
131+
99132
public bool SatisfiesFilter(PluginStoreItemViewModel plugin)
100133
{
101134
// Check plugin language

Flow.Launcher/SettingPages/Views/SettingsPanePluginStore.xaml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,13 @@
9292
</ui:MenuFlyout>
9393
</ui:FlyoutService.Flyout>
9494
</Button>
95+
<Button
96+
Height="34"
97+
Margin="0 0 10 0"
98+
Command="{Binding InstallPluginCommand}"
99+
ToolTip="{DynamicResource installLocalPluginTooltip}">
100+
<ui:FontIcon FontSize="14" Glyph="&#xE8DA;" />
101+
</Button>
95102
<TextBox
96103
Name="PluginStoreFilterTextbox"
97104
Width="150"

0 commit comments

Comments
 (0)