소스 검색

Added "Check For Updates" and "Open Support Session" to System Menu and Support Tickets
"Support Tickets" button is now colored if any "waiting Tickets for Current User are present

frankvandenbos 8 달 전
부모
커밋
89f75ee929

+ 28 - 5
prs.desktop/Forms/Issues/IssuesGrid.cs

@@ -1,19 +1,17 @@
 using Comal.Classes;
-using InABox.Clients;
-using InABox.Configuration;
 using InABox.Core;
 using InABox.DynamicGrid;
-using InABox.Wpf.Editors;
 using InABox.WPF;
 using System;
 using System.Collections.Generic;
-using System.Diagnostics.CodeAnalysis;
+using System.Diagnostics;
 using System.IO;
 using System.Linq;
-using System.Text;
 using System.Threading;
 using System.Threading.Tasks;
+using System.Windows.Controls;
 using System.Windows.Media;
+using InABox.Wpf;
 
 namespace PRSDesktop.Forms.Issues;
 
@@ -99,6 +97,30 @@ public class IssuesGrid : DynamicGrid<Kanban>, ISpecificGrid
 
     protected override void Init()
     {
+        AddButton("Check for Updates", PRSDesktop.Resources.autoupdate.AsBitmapImage(), CheckForUpdates);
+        AddButton("Open Support Session", PRSDesktop.Resources.appicon.AsBitmapImage(), OpenSupportSession);
+    }
+
+    private bool OpenSupportSession(Button button, CoreRow[] rows)
+    {
+        SupportUtils.OpenSupportSession();
+
+        return false;
+    }
+
+    
+
+    private bool CheckForUpdates(Button button, CoreRow[] rows)
+    {
+        if (!SupportUtils.CheckForUpdates())
+        {
+            if (MessageWindow.ShowYesNo(
+                    "You appear to be using the latest version already!\n\nRun the installer anyway?", "Update"))
+            {
+                SupportUtils.DownloadAndRunInstaller();
+            }
+        }
+        return false;
     }
 
     protected override void DoReconfigure(DynamicGridOptions options)
@@ -159,6 +181,7 @@ public class IssuesGrid : DynamicGrid<Kanban>, ISpecificGrid
         {
             text = string.Format("{0:yyyy-MM-dd HH:mm:ss}: {1}", DateTime.Now, text);
             kanban.Notes = kanban.Notes.Concatenate([text]);
+            kanban.Status = KanbanStatus.InProgress;
             SaveItem(kanban);
             Refresh(false, true);
         }

+ 64 - 26
prs.desktop/Forms/Issues/IssuesWindow.xaml.cs

@@ -37,31 +37,42 @@ public partial class IssuesWindow : Window
     {
         Grid.Refresh(true, true);
     }
+    
+    private static Guid? _customerID = null;
 
-    public static void Execute()
+    private static bool CheckCustomerID()
     {
+        if (_customerID is not null)
+            return true;
+        
         var license = Client.Query(new Filter<License>().All(), Columns.None<License>().Add(x => x.Data))
             .ToObjects<License>().FirstOrDefault();
 
-        var customerID = Guid.Empty;
-        if(license is not null && LicenseUtils.TryDecryptLicense(license.Data, out var result, out var error))
+        if (license is not null && LicenseUtils.TryDecryptLicense(license.Data, out var result, out var error))
         {
-            customerID = result.CustomerID;
-        }
-        if(customerID == Guid.Empty)
-        {
-            MessageWindow.ShowMessage("Could not load issues: no customer ID", "Error");
-            return;
+            _customerID = result.CustomerID;
+            return true;
         }
 
+        return false;
+
+    }
+    
+    private static RpcClientSocketTransport? _transport = null;
+
+    private static bool CheckTransport()
+    {
+        if (_transport is not null)
+            return true;
+        
         var transport = new RpcClientSocketTransport(["remote.prsdigital.com.au:8006"]);
         var client = new RpcClient<Kanban>(transport);
         if(client.Validate("SYSTEM", "DO!pt%Qi!R0_h@LW", Guid.Empty).Status != InABox.Clients.ValidationStatus.VALID)
-        {
-            MessageWindow.ShowMessage("Could not connect to PRS digital database.", "Connection error.");
-            return;
-        }
+            return false;
         
+        _transport = transport;
+        return true;
+
         // var transport = new RpcClientSocketTransport(["127.0.0.1:8000"]);
         // var client = new RpcClient<Kanban>(transport);
         // if(client.Validate("frank", "frank", Guid.Empty).Status != InABox.Clients.ValidationStatus.VALID)
@@ -69,25 +80,52 @@ public partial class IssuesWindow : Window
         //     MessageWindow.ShowMessage("Could not connect to PRS digital database.", "Connection error.");
         //     return;
         // }
+    }
 
-        var issues = new IssuesWindow();
 
-        // Save the old property if it exists.
-        //var prop = DatabaseSchema.Property(typeof(Kanban), IssuesGrid.CustomerProperty.Name) as CustomProperty;
+    public static bool Check()
+    {
+        if (!CheckCustomerID())
+            return false;
 
-        // Load the new property.
-        //DatabaseSchema.Load([IssuesGrid.CustomerProperty]);
+        if (!CheckTransport())
+            return false;
 
-        issues.Grid.CustomerID = customerID;
-        issues.ClientFactory = new _Factory(transport);
+        var client = new RpcClient<Kanban>(_transport!);
+        var waiting = client.Query(
+            new Filter<Kanban>(x => x.Closed).IsEqualTo(Guid.Empty)
+                .And(x => x.Status).IsEqualTo(KanbanStatus.Waiting)
+                .And(x=>x.JobLink.Customer.ID).IsEqualTo(_customerID!.Value)
+                .And(x=>x.CreatedBy).IsEqualTo(App.EmployeeName),
+            Columns.None<Kanban>().Add(x => x.LastUpdate),
+            new SortOrder<Kanban>(x => x.LastUpdate, SortDirection.Descending),
+            CoreRange.Database(1)
+        ).Rows.Any();
+        
+        return waiting;
+
+    }
+
+    public static void Execute()
+    {
+        if (!CheckCustomerID())
+        {
+            MessageWindow.ShowMessage("Could not load issues: no customer ID", "Error");
+            return;
+        }
+
+        if (!CheckTransport())
+        {
+            MessageWindow.ShowMessage("Could not connect to PRS digital database.", "Connection error.");
+            return;
+        }
+        
+        var issues = new IssuesWindow();
+
+        issues.Grid.CustomerID = _customerID!.Value;
+        issues.ClientFactory = new _Factory(_transport!);
         issues.ShowDialog();
 
-        // Return DB schema to what it was.
-        // DatabaseSchema.Unload([IssuesGrid.CustomerProperty]);
-        // if(prop is not null)
-        // {
-        //     DatabaseSchema.Load([prop]);
-        // }
     }
 
     private class _Factory(IRpcClientTransport transport) : IQueryProviderFactory

+ 21 - 2
prs.desktop/MainWindow.xaml

@@ -133,7 +133,25 @@
                             Size="Middle"
                             HorizontalAlignment="Stretch"
                             Foreground="{Binding Path=(themes:ThemeManager.BackstageForegroundBrush)}" />
-
+                        
+                        <fluent:Button
+                            x:Name="OpenSupportSessionButton"
+                            Header="Open Support Session"
+                            Click="OpenSupportSession_OnClick"
+                            Size="Middle"
+                            HorizontalAlignment="Stretch"
+                            Foreground="{Binding Path=(themes:ThemeManager.BackstageForegroundBrush)}" />
+                        
+                        <fluent:SeparatorTabItem x:Name="BackstageSeparator2a" Height="20" />
+                        
+                        <fluent:Button
+                            x:Name="CheckForUpdatesButton"
+                            Header="Check For Updates"
+                            Click="CheckForUpdates_OnClick"
+                            Size="Middle"
+                            HorizontalAlignment="Stretch"
+                            Foreground="{Binding Path=(themes:ThemeManager.BackstageForegroundBrush)}" />
+                        
                         <fluent:SeparatorTabItem x:Name="BackstageSeparator2" Height="20" />
 
                         <fluent:Button
@@ -731,8 +749,9 @@
                            Margin="0,0,5,20"/>
             
             <fluent:Button Grid.Row="1" Grid.Column="2"
+                           x:Name="IssuesButton"
                            Header="Support Tickets"
-                           LargeIcon="Resources/appicon.png"
+                           LargeIcon="pack://application:,,,/Resources/appicon.png"
                            Click="Issues_Click"
                            ToolTip="Raise an issue with the PRS team"
                            Margin="0,0,5,20"/>

+ 98 - 60
prs.desktop/MainWindow.xaml.cs

@@ -61,6 +61,7 @@ using Comal.Classes.SecurityDescriptors;
 using System.Threading;
 using H.Formatters;
 using PRSDesktop.Forms.Issues;
+using Brushes = System.Windows.Media.Brushes;
 
 namespace PRSDesktop;
 
@@ -273,7 +274,7 @@ public partial class MainWindow : IPanelHostControl
         Title = $"{(String.Equals(App.Profile?.ToUpper(), "DEFAULT") ? "PRS Desktop" : App.Profile)} (Release {CoreUtils.GetVersion()})";
         
         Logger.Send(LogType.Information, "", "Checking for updates");
-        CheckForUpdates();
+        SupportUtils.CheckForUpdates();
 
         Exception? startupException = null;
         ValidationStatus? loginStatus = null;
@@ -1641,6 +1642,9 @@ public partial class MainWindow : IPanelHostControl
     /// <param name="progress">If not <see langword="null"/>, then rather than opening a new progress window, just uses that.</param>
     private void AfterLogin(IProgress<string>? progress)
     {
+        Logger.Send(LogType.Information, "", "Checking Support Ticket Status");
+        CheckSupportTicketStatus();
+        
         Logger.Send(LogType.Information, "", "Loading employee");
         LoadCurrentEmployee();
         if (CheckTimesheetBypass(true))
@@ -2391,6 +2395,9 @@ public partial class MainWindow : IPanelHostControl
         //{
         try
         {
+
+            CheckSupportTicketStatus();
+            
             bool IsClockedOn = this.IsClockedOn();
 
             if (IsClockedOn)
@@ -2429,6 +2436,17 @@ public partial class MainWindow : IPanelHostControl
         //});
     }
 
+    private void CheckSupportTicketStatus()
+    {
+        Dispatcher.BeginInvoke(() =>
+        {
+            IssuesButton.Background = IssuesWindow.Check()
+                ? new SolidColorBrush(Colors.Red) { Opacity = 0.5 }
+                : Brushes.Transparent;
+
+        });
+    }
+
     private void Notifications_Changed(object sender)
     {
         if (Notifications.IsActive)
@@ -2887,64 +2905,64 @@ public partial class MainWindow : IPanelHostControl
     [DllImport("user32.dll")]
     private static extern IntPtr GetActiveWindow();
 
-    #region Check For Updates
-
-    private string GetUpdateLocation()
-    {
-        if (App.DatabaseSettings.DatabaseType == DatabaseType.Networked)
-        {
-            if(ClientFactory.ClientType == typeof(RestClient<>))
-            {
-                string url = "";
-                //var domain = App.DatabaseSettings.URL.Split(new[] { "://" }, StringSplitOptions.RemoveEmptyEntries).Last();
-                //var port = App.DatabaseSettings.Port;
-                var domain = ClientFactory.Parameters?.FirstOrDefault()?.ToString() ?? "";
-                domain = domain.Split(new[] { "://" }, StringSplitOptions.RemoveEmptyEntries).LastOrDefault() ?? "";
-                if (!String.IsNullOrWhiteSpace(domain))
-                {
-                    
-                    try
-                    {
-                        var client = new HttpClient { BaseAddress = new Uri($"https://{domain}") };
-                        client.GetAsync("operations").Wait();
-                        url = $"https://{domain}";
-                    }
-                    catch (Exception)
-                    {
-                        url = $"http://{domain}";
-                    }
-                }
-                return url;
-            }
-            else
-            {
-                return "";
-            }
-        }
-        else
-            return Path.Combine(CoreUtils.GetCommonAppData("PRSServer"), "update");
-    }
-
-    private string GetLatestVersion(string location)
-    {
-        return Client.Version();
-    }
-    private string GetReleaseNotes(string location)
-    {
-        return Client.ReleaseNotes();
-    }
-    private byte[]? GetInstaller(string location)
-    {
-        return Client.Installer();
-    }
-
-    private void CheckForUpdates()
-    {
-        Update.CheckForUpdates(
-            GetUpdateLocation, GetLatestVersion, GetReleaseNotes, GetInstaller, null, App.AutoUpdateSettings.Elevated, "PRSDesktopSetup.exe");
-    }
-
-    #endregion
+    // #region Check For Updates
+    //
+    // private string GetUpdateLocation()
+    // {
+    //     if (App.DatabaseSettings.DatabaseType == DatabaseType.Networked)
+    //     {
+    //         if(ClientFactory.ClientType == typeof(RestClient<>))
+    //         {
+    //             string url = "";
+    //             //var domain = App.DatabaseSettings.URL.Split(new[] { "://" }, StringSplitOptions.RemoveEmptyEntries).Last();
+    //             //var port = App.DatabaseSettings.Port;
+    //             var domain = ClientFactory.Parameters?.FirstOrDefault()?.ToString() ?? "";
+    //             domain = domain.Split(new[] { "://" }, StringSplitOptions.RemoveEmptyEntries).LastOrDefault() ?? "";
+    //             if (!String.IsNullOrWhiteSpace(domain))
+    //             {
+    //                 
+    //                 try
+    //                 {
+    //                     var client = new HttpClient { BaseAddress = new Uri($"https://{domain}") };
+    //                     client.GetAsync("operations").Wait();
+    //                     url = $"https://{domain}";
+    //                 }
+    //                 catch (Exception)
+    //                 {
+    //                     url = $"http://{domain}";
+    //                 }
+    //             }
+    //             return url;
+    //         }
+    //         else
+    //         {
+    //             return "";
+    //         }
+    //     }
+    //     else
+    //         return Path.Combine(CoreUtils.GetCommonAppData("PRSServer"), "update");
+    // }
+    //
+    // private string GetLatestVersion(string location)
+    // {
+    //     return Client.Version();
+    // }
+    // private string GetReleaseNotes(string location)
+    // {
+    //     return Client.ReleaseNotes();
+    // }
+    // private byte[]? GetInstaller(string location)
+    // {
+    //     return Client.Installer();
+    // }
+    //
+    // private void CheckForUpdates()
+    // {
+    //     Update.CheckForUpdates(
+    //         GetUpdateLocation, GetLatestVersion, GetReleaseNotes, GetInstaller, null, App.AutoUpdateSettings.Elevated, "PRSDesktopSetup.exe");
+    // }
+    //
+    // #endregion
 
     #region Modules + Reports
 
@@ -3304,6 +3322,23 @@ public partial class MainWindow : IPanelHostControl
             MessageBox.Show(logfile + " does not exist!");
         }
     }
+    
+    private void CheckForUpdates_OnClick(object sender, RoutedEventArgs e)
+    {
+        if (!SupportUtils.CheckForUpdates())
+        {
+            if (MessageWindow.ShowYesNo(
+                    "You appear to be using the latest version already!\n\nRun the installer anyway?", "Update"))
+            {
+                SupportUtils.DownloadAndRunInstaller();
+            }
+        }
+    }
+    
+    private void OpenSupportSession_OnClick(object sender, RoutedEventArgs e)
+    {
+        SupportUtils.OpenSupportSession();
+    }
 
     private void DocumentTypeList_OnClick(object sender, RoutedEventArgs e)
     {
@@ -3386,11 +3421,14 @@ public partial class MainWindow : IPanelHostControl
         try
         {
             IssuesWindow.Execute();
+            CheckSupportTicketStatus();
         }
         catch(Exception err)
         {
             MessageWindow.ShowError("Could not load issues.", err);
         }
     }
-    
+
+
+
 }

+ 5 - 0
prs.desktop/PRSDesktop.csproj

@@ -1060,6 +1060,11 @@
         <XamlRuntime>Wpf</XamlRuntime>
         <SubType>Designer</SubType>
       </Page>
+      <Page Update="Utils\DownloadSupportAppWindow.xaml">
+        <Generator>MSBuild:Compile</Generator>
+        <XamlRuntime>Wpf</XamlRuntime>
+        <SubType>Designer</SubType>
+      </Page>
     </ItemGroup>
 
     <Import Project="..\PRS.Scheduler\Comal.TaskScheduler.Shared.projitems" Label="Shared" />

+ 4 - 1
prs.desktop/Panels/Tasks/TaskGrid.cs

@@ -216,7 +216,10 @@ public class TaskGrid : DynamicDataGrid<Kanban>, ITaskControl, IDefaultGrid
 
     public DataModel DataModel(Selection selection)
     {
-        return new AutoDataModel<Kanban>(new Filter<Kanban>(x => x.ID).IsEqualTo(Guid.Empty));
+        var ids = ExtractValues<Guid>(x => x.ID, selection);
+        if (!ids.Any())
+            ids = [Guid.Empty];
+        return new AutoDataModel<Kanban>(new Filter<Kanban>(x => x.ID).InList(ids.ToArray()));
     }
 
     public void Refresh()

+ 48 - 0
prs.desktop/Utils/DownloadSupportAppWindow.xaml

@@ -0,0 +1,48 @@
+<Window x:Class="PRSDesktop.DownloadSupportAppWindow"
+        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+        xmlns:local="clr-namespace:PRSDesktop"
+        xmlns:wpf="clr-namespace:Microsoft.Web.WebView2.Wpf;assembly=Microsoft.Web.WebView2.Wpf"
+        mc:Ignorable="d"
+        Title="DownloadSupportAppWindow" Height="450" Width="800">
+    <Grid 
+        Margin="5">
+        <Grid.ColumnDefinitions>
+            <ColumnDefinition Width="Auto"/>
+            <ColumnDefinition Width="*" />
+            <ColumnDefinition Width="Auto"/>
+        </Grid.ColumnDefinitions>
+        <Grid.RowDefinitions>
+            <RowDefinition Height="Auto" />
+            <RowDefinition Height="*" />
+        </Grid.RowDefinitions>
+        <Label
+            Content="Paste URL:"
+            Grid.Row="0"
+            Grid.Column="0"
+            Margin="0,0,5,0"
+            HorizontalContentAlignment="Center"
+            VerticalContentAlignment="Center"/>
+        <TextBox
+            x:Name="URL"
+            Grid.Row="0"
+            Grid.Column="1"
+            VerticalContentAlignment="Center"/>
+        <Button
+            Content=".."
+            Grid.Row="0"
+            Grid.Column="2"
+            Padding="10,0"
+            Margin="5,0,0,0"
+            Click="Download_Click"/>
+        <wpf:WebView2
+            x:Name="Browser"
+            Grid.Row="1"
+            Grid.Column="0"
+            Grid.ColumnSpan="3"
+            Margin="0,5,0,0"
+            NavigationStarting="Browser_OnNavigationStarting" />
+    </Grid> 
+</Window>

+ 38 - 0
prs.desktop/Utils/DownloadSupportAppWindow.xaml.cs

@@ -0,0 +1,38 @@
+using System;
+using System.IO;
+using System.Windows;
+using InABox.Core;
+using Microsoft.Web.WebView2.Core;
+
+namespace PRSDesktop;
+
+public partial class DownloadSupportAppWindow : Window
+{
+    public DownloadSupportAppWindow()
+    {
+        InitializeComponent();
+    }
+
+    private void Download_Click(object sender, RoutedEventArgs e)
+    {
+        Browser.Source = new Uri(URL.Text);
+    }
+
+    private void Browser_OnNavigationStarting(object? sender, CoreWebView2NavigationStartingEventArgs e)
+    {
+        Browser.CoreWebView2.DownloadStarting += (o, args) =>
+        {
+            args.ResultFilePath = System.IO.Path.Combine(CoreUtils.GetPath(), "helpwire.exe");
+            args.DownloadOperation.StateChanged += (o, e) =>
+            {
+                if (args.DownloadOperation.State == CoreWebView2DownloadState.Interrupted)
+                {
+                    if (File.Exists(args.ResultFilePath))
+                        File.Delete(args.ResultFilePath);
+                }
+                if (args.DownloadOperation.State == CoreWebView2DownloadState.Completed)
+                    Close();
+            };
+        };
+    }
+}

+ 119 - 0
prs.desktop/Utils/SupportUtils.cs

@@ -0,0 +1,119 @@
+using System;
+using System.Diagnostics;
+using System.IO;
+using System.Linq;
+using System.Net.Http;
+using Comal.Classes;
+using InABox.Clients;
+using InABox.Core;
+using InABox.Wpf;
+using PRS.Shared;
+
+namespace PRSDesktop;
+
+public static class SupportUtils
+{
+    
+    public static void OpenSupportSession()
+    {
+        var helpwire = Path.Combine(CoreUtils.GetPath(), "helpwire.exe");
+        if (!File.Exists(helpwire))
+        {
+            DownloadSupportAppWindow dl = new DownloadSupportAppWindow();
+            dl.ShowDialog();
+        }
+        if (File.Exists(helpwire))
+        {
+            var startInfo = new ProcessStartInfo(helpwire);
+            startInfo.Verb = "open";
+            startInfo.UseShellExecute = true;
+            startInfo.WindowStyle = ProcessWindowStyle.Hidden;
+            try
+            {
+                Process.Start(startInfo);
+            }
+            catch (System.Exception e)
+            {
+                Logger.Send(LogType.Information,"",$"{e.Message}\n{e.StackTrace}");
+                MessageWindow.ShowError("Unable to launch Support App", e, "Error");
+                try
+                {
+                    if (File.Exists(helpwire))
+                        File.Delete(helpwire);
+                }
+                catch (System.Exception e2)
+                {
+                    Logger.Send(LogType.Information,"",$"{e2.Message}\n{e2.StackTrace}");
+                }
+            }
+        }
+    }
+    
+    public static string GetUpdateLocation()
+    {
+        if (App.DatabaseSettings.DatabaseType == DatabaseType.Networked)
+        {
+            if(ClientFactory.ClientType == typeof(RestClient<>))
+            {
+                string url = "";
+                //var domain = App.DatabaseSettings.URL.Split(new[] { "://" }, StringSplitOptions.RemoveEmptyEntries).Last();
+                //var port = App.DatabaseSettings.Port;
+                var domain = ClientFactory.Parameters?.FirstOrDefault()?.ToString() ?? "";
+                domain = domain.Split(new[] { "://" }, StringSplitOptions.RemoveEmptyEntries).LastOrDefault() ?? "";
+                if (!String.IsNullOrWhiteSpace(domain))
+                {
+                    
+                    try
+                    {
+                        var client = new HttpClient { BaseAddress = new Uri($"https://{domain}") };
+                        client.GetAsync("operations").Wait();
+                        url = $"https://{domain}";
+                    }
+                    catch (Exception)
+                    {
+                        url = $"http://{domain}";
+                    }
+                }
+                return url;
+            }
+            else
+            {
+                return "";
+            }
+        }
+        else
+            return Path.Combine(CoreUtils.GetCommonAppData("PRSServer"), "update");
+    }
+    
+    public static string GetLatestVersion(string location)
+    {
+        return Client.Version();
+    }
+    
+    public static string GetReleaseNotes(string location)
+    {
+        return Client.ReleaseNotes();
+    }
+    
+    public static byte[]? GetInstaller(string location)
+    {
+        return Client.Installer();
+    }
+    
+    public static bool CheckForUpdates()
+    {
+        return Update.CheckForUpdates(
+            GetUpdateLocation, GetLatestVersion, GetReleaseNotes, GetInstaller, null, App.AutoUpdateSettings.Elevated, "PRSDesktopSetup.exe");
+    }
+
+    public static void DownloadAndRunInstaller()
+    {
+        InABox.WPF.Progress.ShowModal("Retrieving Update", progress =>
+        {
+            Update.DownloadAndRunInstaller(GetInstaller, null, App.AutoUpdateSettings.Elevated, GetUpdateLocation(),
+                "PRSDesktopSetup.exe", progress);
+        });
+
+    }
+
+}

+ 39 - 30
prs.shared/Update.cs

@@ -121,38 +121,9 @@ namespace PRS.Shared
                     return true;
                 
                 var bOK = false;
-                var tempinstall = Path.Combine(CoreUtils.GetPath(), tempName);
                 Progress.ShowModal("Retrieving Update", progress =>
                 {
-                    try
-                    {
-                        var installer = getInstaller(location);
-                        if (installer?.Any() == true)
-                        {
-                            File.WriteAllBytes(tempinstall, installer);
-
-                            var scriptFile = Path.Combine(CoreUtils.GetPath(), "install.bat");
-                            File.WriteAllText(scriptFile, GenerateInstallerScript(tempinstall));
-
-                            bOK = true;
-
-                            beforeUpdate?.Invoke();
-                            progress.Report("Launching Installer");
-                            /*var startInfo = new ProcessStartInfo(tempinstall,
-                                " /SILENT /SUPPRESSMSGBOXES /FORCECLOSEAPPLICATIONS /RESTARTAPPLICATIONS");*/
-                            var startInfo = new ProcessStartInfo(scriptFile);
-                            startInfo.Verb = elevated ? "runas" : "open";
-                            startInfo.UseShellExecute = true;
-                            startInfo.WindowStyle = ProcessWindowStyle.Hidden;
-                            var p = Process.Start(startInfo);
-                            p.WaitForExit();
-                        }
-                    }
-                    catch (Exception e)
-                    {
-                        Logger.Send(LogType.Error, "", CoreUtils.FormatException(e));
-                        MessageBox.Show("Error during installation!");
-                    }
+                    bOK = DownloadAndRunInstaller(getInstaller, beforeUpdate, elevated, location, tempName, progress);
                 });
                 if (bOK)
                     return true;
@@ -162,5 +133,43 @@ namespace PRS.Shared
             }
             return false;
         }
+
+        public static bool DownloadAndRunInstaller(Func<string, byte[]?>? getInstaller, Action? beforeUpdate, bool elevated, string location,
+            string tempName, IProgress<string> progress)
+        {
+            bool bOK = false;
+            var tempinstall = Path.Combine(CoreUtils.GetPath(), tempName);
+            try
+            {
+                var installer = getInstaller(location);
+                if (installer?.Any() == true)
+                {
+                    File.WriteAllBytes(tempinstall, installer);
+
+                    var scriptFile = Path.Combine(CoreUtils.GetPath(), "install.bat");
+                    File.WriteAllText(scriptFile, GenerateInstallerScript(tempinstall));
+
+                    bOK = true;
+
+                    beforeUpdate?.Invoke();
+                    progress.Report("Launching Installer");
+                    /*var startInfo = new ProcessStartInfo(tempinstall,
+                                " /SILENT /SUPPRESSMSGBOXES /FORCECLOSEAPPLICATIONS /RESTARTAPPLICATIONS");*/
+                    var startInfo = new ProcessStartInfo(scriptFile);
+                    startInfo.Verb = elevated ? "runas" : "open";
+                    startInfo.UseShellExecute = true;
+                    startInfo.WindowStyle = ProcessWindowStyle.Hidden;
+                    var p = Process.Start(startInfo);
+                    p.WaitForExit();
+                }
+            }
+            catch (Exception e)
+            {
+                Logger.Send(LogType.Error, "", CoreUtils.FormatException(e));
+                MessageBox.Show("Error during installation!");
+            }
+
+            return bOK;
+        }
     }
 }