22using System . Collections . Concurrent ;
33using System . Collections . Generic ;
44using System . IO ;
5- using System . IO . Compression ;
65using System . Linq ;
76using System . Text . Json ;
87using System . Threading ;
98using System . Threading . Tasks ;
10- using System . Windows ;
119using CommunityToolkit . Mvvm . DependencyInjection ;
1210using Flow . Launcher . Core . ExternalPlugins ;
1311using Flow . Launcher . Infrastructure ;
@@ -26,8 +24,6 @@ public static class PluginManager
2624 {
2725 private static readonly string ClassName = nameof ( PluginManager ) ;
2826
29- private static readonly Settings FlowSettings = Ioc . Default . GetRequiredService < Settings > ( ) ;
30-
3127 private static IEnumerable < PluginPair > _contextMenuPlugins ;
3228 private static IEnumerable < PluginPair > _homePlugins ;
3329
@@ -561,213 +557,6 @@ public static async Task UninstallPluginAsync(PluginMetadata plugin, bool remove
561557 await UninstallPluginAsync ( plugin , removePluginFromSettings , removePluginSettings , true ) ;
562558 }
563559
564- public static async Task InstallPluginAndCheckRestartAsync ( UserPlugin newPlugin )
565- {
566- if ( API . ShowMsgBox (
567- string . Format (
568- API . GetTranslation ( "InstallPromptSubtitle" ) ,
569- newPlugin . Name , newPlugin . Author , Environment . NewLine ) ,
570- API . GetTranslation ( "InstallPromptTitle" ) ,
571- button : MessageBoxButton . YesNo ) != MessageBoxResult . Yes ) return ;
572-
573- try
574- {
575- // at minimum should provide a name, but handle plugin that is not downloaded from plugins manifest and is a url download
576- var downloadFilename = string . IsNullOrEmpty ( newPlugin . Version )
577- ? $ "{ newPlugin . Name } -{ Guid . NewGuid ( ) } .zip"
578- : $ "{ newPlugin . Name } -{ newPlugin . Version } .zip";
579-
580- var filePath = Path . Combine ( Path . GetTempPath ( ) , downloadFilename ) ;
581-
582- using var cts = new CancellationTokenSource ( ) ;
583-
584- if ( ! newPlugin . IsFromLocalInstallPath )
585- {
586- await DownloadFileAsync (
587- $ "{ API . GetTranslation ( "DownloadingPlugin" ) } { newPlugin . Name } ",
588- newPlugin . UrlDownload , filePath , cts ) ;
589- }
590- else
591- {
592- filePath = newPlugin . LocalInstallPath ;
593- }
594-
595- // check if user cancelled download before installing plugin
596- if ( cts . IsCancellationRequested )
597- {
598- return ;
599- }
600- else
601- {
602- if ( ! File . Exists ( filePath ) )
603- {
604- throw new FileNotFoundException ( $ "Plugin { newPlugin . ID } zip file not found at { filePath } ", filePath ) ;
605- }
606-
607- API . InstallPlugin ( newPlugin , filePath ) ;
608-
609- if ( ! newPlugin . IsFromLocalInstallPath )
610- {
611- File . Delete ( filePath ) ;
612- }
613- }
614- }
615- catch ( Exception e )
616- {
617- API . LogException ( ClassName , "Failed to install plugin" , e ) ;
618- API . ShowMsgError ( API . GetTranslation ( "ErrorInstallingPlugin" ) ) ;
619- return ; // don’t restart on failure
620- }
621-
622- if ( FlowSettings . AutoRestartAfterChanging )
623- {
624- API . RestartApp ( ) ;
625- }
626- else
627- {
628- API . ShowMsg (
629- API . GetTranslation ( "installbtn" ) ,
630- string . Format (
631- API . GetTranslation (
632- "InstallSuccessNoRestart" ) ,
633- newPlugin . Name ) ) ;
634- }
635- }
636-
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-
673- public static async Task UninstallPluginAndCheckRestartAsync ( PluginMetadata oldPlugin )
674- {
675- if ( API . ShowMsgBox (
676- string . Format (
677- API . GetTranslation ( "UninstallPromptSubtitle" ) ,
678- oldPlugin . Name , oldPlugin . Author , Environment . NewLine ) ,
679- API . GetTranslation ( "UninstallPromptTitle" ) ,
680- button : MessageBoxButton . YesNo ) != MessageBoxResult . Yes ) return ;
681-
682- var removePluginSettings = API . ShowMsgBox (
683- API . GetTranslation ( "KeepPluginSettingsSubtitle" ) ,
684- API . GetTranslation ( "KeepPluginSettingsTitle" ) ,
685- button : MessageBoxButton . YesNo ) == MessageBoxResult . No ;
686-
687- try
688- {
689- await API . UninstallPluginAsync ( oldPlugin , removePluginSettings ) ;
690- }
691- catch ( Exception e )
692- {
693- API . LogException ( ClassName , "Failed to uninstall plugin" , e ) ;
694- API . ShowMsgError ( API . GetTranslation ( "ErrorUninstallingPlugin" ) ) ;
695- return ; // don’t restart on failure
696- }
697-
698- if ( FlowSettings . AutoRestartAfterChanging )
699- {
700- API . RestartApp ( ) ;
701- }
702- else
703- {
704- API . ShowMsg (
705- API . GetTranslation ( "uninstallbtn" ) ,
706- string . Format (
707- API . GetTranslation (
708- "UninstallSuccessNoRestart" ) ,
709- oldPlugin . Name ) ) ;
710- }
711- }
712-
713- public static async Task UpdatePluginAndCheckRestartAsync ( UserPlugin newPlugin , PluginMetadata oldPlugin )
714- {
715- if ( API . ShowMsgBox (
716- string . Format (
717- API . GetTranslation ( "UpdatePromptSubtitle" ) ,
718- oldPlugin . Name , oldPlugin . Author , Environment . NewLine ) ,
719- API . GetTranslation ( "UpdatePromptTitle" ) ,
720- button : MessageBoxButton . YesNo ) != MessageBoxResult . Yes ) return ;
721-
722- try
723- {
724- var filePath = Path . Combine ( Path . GetTempPath ( ) , $ "{ newPlugin . Name } -{ newPlugin . Version } .zip") ;
725-
726- using var cts = new CancellationTokenSource ( ) ;
727-
728- if ( ! newPlugin . IsFromLocalInstallPath )
729- {
730- await DownloadFileAsync (
731- $ "{ API . GetTranslation ( "DownloadingPlugin" ) } { newPlugin . Name } ",
732- newPlugin . UrlDownload , filePath , cts ) ;
733- }
734- else
735- {
736- filePath = newPlugin . LocalInstallPath ;
737- }
738-
739- // check if user cancelled download before installing plugin
740- if ( cts . IsCancellationRequested )
741- {
742- return ;
743- }
744- else
745- {
746- await API . UpdatePluginAsync ( oldPlugin , newPlugin , filePath ) ;
747- }
748- }
749- catch ( Exception e )
750- {
751- API . LogException ( ClassName , "Failed to update plugin" , e ) ;
752- API . ShowMsgError ( API . GetTranslation ( "ErrorUpdatingPlugin" ) ) ;
753- return ; // don’t restart on failure
754- }
755-
756- if ( FlowSettings . AutoRestartAfterChanging )
757- {
758- API . RestartApp ( ) ;
759- }
760- else
761- {
762- API . ShowMsg (
763- API . GetTranslation ( "updatebtn" ) ,
764- string . Format (
765- API . GetTranslation (
766- "UpdateSuccessNoRestart" ) ,
767- newPlugin . Name ) ) ;
768- }
769- }
770-
771560 #endregion
772561
773562 #region Internal functions
@@ -915,59 +704,6 @@ internal static async Task UninstallPluginAsync(PluginMetadata plugin, bool remo
915704 }
916705 }
917706
918- internal static async Task DownloadFileAsync ( string prgBoxTitle , string downloadUrl , string filePath , CancellationTokenSource cts , bool deleteFile = true , bool showProgress = true )
919- {
920- if ( deleteFile && File . Exists ( filePath ) )
921- File . Delete ( filePath ) ;
922-
923- if ( showProgress )
924- {
925- var exceptionHappened = false ;
926- await API . ShowProgressBoxAsync ( prgBoxTitle ,
927- async ( reportProgress ) =>
928- {
929- if ( reportProgress == null )
930- {
931- // when reportProgress is null, it means there is expcetion with the progress box
932- // so we record it with exceptionHappened and return so that progress box will close instantly
933- exceptionHappened = true ;
934- return ;
935- }
936- else
937- {
938- await API . HttpDownloadAsync ( downloadUrl , filePath , reportProgress , cts . Token ) . ConfigureAwait ( false ) ;
939- }
940- } , cts . Cancel ) ;
941-
942- // if exception happened while downloading and user does not cancel downloading,
943- // we need to redownload the plugin
944- if ( exceptionHappened && ( ! cts . IsCancellationRequested ) )
945- await API . HttpDownloadAsync ( downloadUrl , filePath , token : cts . Token ) . ConfigureAwait ( false ) ;
946- }
947- else
948- {
949- await API . HttpDownloadAsync ( downloadUrl , filePath , token : cts . Token ) . ConfigureAwait ( false ) ;
950- }
951- }
952-
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-
971707 #endregion
972708 }
973709}
0 commit comments