using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; using System.Diagnostics; using System.Drawing; using System.Drawing.Drawing2D; using System.Drawing.Imaging; using System.IO; using System.Linq; using System.Net.Http; using System.Reflection; using System.Runtime.InteropServices; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Forms; using System.Windows.Input; using System.Windows.Interop; using System.Windows.Media; using System.Windows.Threading; using AvalonDock.Layout; using Comal.Classes; using Comal.Stores; using Comal.TaskScheduler.Shared; using H.Pipes; using InABox.Clients; using InABox.Configuration; using InABox.Core; using InABox.Database; using InABox.Database.SQLite; using InABox.DynamicGrid; using InABox.Mail; using InABox.Core.Reports; using InABox.IPC; using InABox.Rpc; using InABox.Scripting; using InABox.Wpf; using InABox.Wpf.Console; using InABox.WPF; using NAudio.Wave; using PRS.Shared; using InABox.WPF.Themes; using PRSDesktop.Configuration; using PRSDesktop.Forms; using PRSServer; using SharpAvi.Codecs; using SharpAvi.Output; using Syncfusion.Windows.Shared; using Syncfusion.Windows.Tools; using Syncfusion.Windows.Tools.Controls; using Activity = Comal.Classes.Activity; using Application = System.Windows.Application; using ButtonBase = System.Windows.Controls.Primitives.ButtonBase; using Color = System.Windows.Media.Color; using ColorConverter = System.Windows.Media.ColorConverter; using Control = System.Windows.Controls.Control; using Image = System.Drawing.Image; using KeyEventArgs = System.Windows.Input.KeyEventArgs; using MessageBox = System.Windows.MessageBox; using Pen = System.Drawing.Pen; using PixelFormat = System.Drawing.Imaging.PixelFormat; using Role = Comal.Classes.Role; using SortDirection = InABox.Core.SortDirection; using PRSDesktop.Components.Spreadsheet; using InABox.Wpf.Reports; using Comal.Classes.SecurityDescriptors; using System.Threading; using System.Windows.Data; using System.Windows.Media.Imaging; using H.Formatters; using PRSDesktop.Panels; using Syncfusion.SfSkinManager; namespace PRSDesktop; /// /// Interaction logic for Main.xaml /// public partial class MainWindow : IPanelHostControl { //private const int WM_LBUTTONDOWN = 0x201; private static PipeServer? _client; private IRpcClientTransport? _transport; private WaveIn? _audio; private bool _audioMuted; private MemoryStream? _audioStream; private readonly Dictionary _bitmaps = new(); private DesktopConsole? _console; private Dictionary> _notes = new(); private DispatcherTimer? _recorder; private Process? _recordingnotes; private int _screenheight = 720; private int _screenleft; private int _screentop; private int _screenwidth = 1280; private readonly Dictionary SecondaryWindows = new(); private CoreTable? _timesheets; private DailyActivityHistory? ActivityHistory; private readonly List CurrentModules = new(); private Fluent.RibbonTabItem? CurrentTab; private Fluent.Button? CurrentButton; private readonly int FRAMES_PER_SECOND = 10; private DatabaseType DatabaseType; private readonly Dictionary messages = new(); private readonly DispatcherTimer NotificationsWatchDog; private DateTime pausestarted = DateTime.MinValue; private readonly Scheduler scheduler = new() { Interval = new TimeSpan(0, 5, 0) }; // We use a Guid for the StationID rather than an IP or Mac address // because we want true single-instance restriction. Using either of // the above allows for two instances on the once machine, and thus // double-counting in the Heartbeat() function private Login station = new() { StationID = Guid.NewGuid().ToString() }; private TimeSpan totalpauses = new(0); private readonly int VIDEO_HEIGHT = 1080; private readonly int VIDEO_WIDTH = 1920; private PanelHost PanelHost; public MainWindow() { PanelHost = new PanelHost(this); NotificationsWatchDog = new DispatcherTimer { IsEnabled = false }; NotificationsWatchDog.Tick += Notifications_Tick; NotificationsWatchDog.Interval = new TimeSpan(0, 2, 0); ClientFactory.PushHandlers.AddHandler(ReceiveNotification); ClientFactory.RegisterMailer(EmailType.IMAP, typeof(IMAPMailer)); ClientFactory.RegisterMailer(EmailType.Exchange, typeof(ExchangeMailer)); ClientFactory.OnLog += (type, userid, message, parameters) => Logger.Send(LogType.Information, ClientFactory.UserID, message, parameters); ClientFactory.OnRequestError += ClientFactory_OnRequestError; HotKeyManager.Initialize(); HotKeyManager.RegisterHotKey(Key.F1, ShowHelp); //HotKeyManager.RegisterHotKey(Key.F5, ToggleRecording); //HotKeyManager.RegisterHotKey(Key.F6, ShowRecordingNotes); //HotKeyManager.RegisterHotKey(Key.F4, ToggleRecordingAudio); Logger.Send(LogType.Information, "", "Connecting to server"); var settings = App.DatabaseSettings; bool dbConnected; DatabaseType = settings.DatabaseType; switch (DatabaseType) { case DatabaseType.Standalone: ClientFactory.SetClientType(typeof(LocalClient<>), Platform.Wpf, CoreUtils.GetVersion()); DbFactory.ColorScheme = App.DatabaseSettings.ColorScheme; DbFactory.Logo = App.DatabaseSettings.Logo; dbConnected = true; break; case DatabaseType.Networked: if (App.DatabaseSettings.Protocol == SerializerProtocol.RPC) { _transport = new RpcClientSocketTransport(App.DatabaseSettings.URLs); _transport.OnClose += TransportConnectionLost; _transport.OnException += Transport_OnException; _transport.OnOpen += Transport_OnOpen; ; dbConnected = _transport.Connect(); ClientFactory.SetClientType(typeof(RpcClient<>), Platform.Wpf, CoreUtils.GetVersion(), _transport); } else { var url = RestClient.Ping(App.DatabaseSettings.URLs, out DatabaseInfo info); ClientFactory.SetClientType(typeof(RestClient<>), Platform.Wpf, CoreUtils.GetVersion(), url, true); dbConnected = true; } break; case DatabaseType.Local: //new RPC stuff (temporary disabled - for enabling when RPC is ready) var pipename = DatabaseServerProperties.GetPipeName(App.DatabaseSettings.LocalServerName, true); _transport = new RpcClientPipeTransport(pipename); _transport.OnClose += TransportConnectionLost; dbConnected = _transport.Connect(); ClientFactory.SetClientType(typeof(RpcClient<>), Platform.Wpf, CoreUtils.GetVersion(), _transport ); //ClientFactory.SetClientType(typeof(IPCClient<>), Platform.Wpf, CoreUtils.GetVersion(), // DatabaseServerProperties.GetPipeName(App.DatabaseSettings.LocalServerName, false)); //dbConnected = true; break; default: throw new Exception($"Invalid database type {DatabaseType}"); } InitializeComponent(); if (!dbConnected) { switch (DoConnectionFailed()) { case ConnectionFailedResult.Quit: Close(); return; case ConnectionFailedResult.Restart: App.ShouldRestart = true; Close(); return; case ConnectionFailedResult.Ok: // Do nothing break; } } ThemeManager.BaseColor = Colors.CornflowerBlue; Progress.DisplayImage = PRSDesktop.Resources.splash_small.AsBitmapImage(); try { var dbInfo = new Client().Info(); ClientFactory.DatabaseID = dbInfo.DatabaseID; ThemeManager.BaseColor = (Color)ColorConverter.ConvertFromString(dbInfo.ColorScheme); if (dbInfo.Logo?.Any() == true) using (var ms = new MemoryStream(dbInfo.Logo)) { Progress.DisplayImage = new Bitmap(ms).AsBitmapImage(); } } catch { } VideoRecordingStatus.Source = PRSDesktop.Resources.videorecording.AsGrayScale().AsBitmapImage(); AudioRecordingStatus.Source = PRSDesktop.Resources.audiorecording.AsGrayScale().AsBitmapImage(); SecondaryWindowStatus.Source = PRSDesktop.Resources.target.AsGrayScale().AsBitmapImage(); ConsoleStatus.Source = PRSDesktop.Resources.view.AsGrayScale().AsBitmapImage(); SelectTask.Source = PRSDesktop.Resources.uparrow.Invert().AsBitmapImage(); Title = $"{(String.Equals(App.Profile?.ToUpper(), "DEFAULT") ? "PRS Desktop" : App.Profile)} (Release {CoreUtils.GetVersion()})"; Logger.Send(LogType.Information, "", "Checking for updates"); CheckForUpdates(); Exception? startupException = null; ValidationStatus? loginStatus = null; Progress.ShowModal("Loading PRS", progress => { DynamicGridUtils.PreviewReport = (t, m) => { ReportUtils.PreviewReport(t, m, false, Security.IsAllowed()); }; DynamicGridUtils.PrintMenu = (e, s, m, p) => { ReportUtils.PrintMenu(e, s, m, Security.IsAllowed(), p); }; ImportFactory.Register(typeof(ExcelImporter<>), "Excel File", "Excel Files (*.xls;*.xlsx;*.xlsm)|*.xls;*.xlsx;*.xlsm"); ImportFactory.Register(typeof(CustomImporter<>), "Custom", "All Files (*.*)|*.*"); FormUtils.Register(); DigitalFormDocumentFactory.Init( new WpfDigitalFormDocumentHandler( b => Dispatcher.BeginInvoke(() => { BackgroundUploadStatus.Visibility = b ? Visibility.Visible : Visibility.Hidden; } ), () => _transport?.IsConnected() ?? false ) ); DigitalFormDocumentFactory.Run(); Logger.Send(LogType.Information, "", "Registering Classes"); progress.Report("Registering Classes"); var tasks = new List { Task.Run(() => { CoreUtils.RegisterClasses(typeof(TaskGrid).Assembly); CoreUtils.RegisterClasses(); ComalUtils.RegisterClasses(); PRSSharedUtils.RegisterClasses(); ReportUtils.RegisterClasses(); ConfigurationUtils.RegisterClasses(); DynamicGridUtils.RegisterClasses(); }), Task.Run(() => { ScriptDocument.DefaultAssemblies.AddRange( Assembly.Load("RoslynPad.Roslyn.Windows"), Assembly.Load("RoslynPad.Editor.Windows"), typeof(Control).Assembly, typeof(MessageBox).Assembly, typeof(SolidColorBrush).Assembly ); ScriptDocument.Initialize(); }), Task.Run(() => DatabaseUpdateScripts.RegisterScripts()) }; Task.WaitAll(tasks.ToArray()); Logger.Send(LogType.Information, "", "Configuring Application"); progress.Report("Configuring Application"); RegisterModules(progress); if (DatabaseType == DatabaseType.Standalone) { progress.Report("Starting local database..."); try { StartLocalDatabase(progress); } catch (Exception err) { startupException = new Exception( string.Format( "Unable to open database ({0})\n\n{1}\n\n{2}", App.DatabaseSettings.FileName, err.Message, err.StackTrace ) ); } } if (startupException is null && App.DatabaseSettings.Autologin) { try { Logger.Send(LogType.Information, "", "Logging in"); progress.Report("Logging in"); Dispatcher.Invoke(() => { loginStatus = TryAutoLogin(); }); if(loginStatus == ValidationStatus.VALID) { // Do the AfterLogin() here so that we aren't opening and closing progress windows again and again. AfterLogin(progress); } } catch(Exception e) { startupException = e; } } }); if (startupException != null) { MessageWindow.ShowError("Error during startup.", startupException); } // If the login status is valid, then we've already loaded everything, so we don't here. if(loginStatus != ValidationStatus.VALID) { Logger.Send(LogType.Information, "", "Logging in"); if (DoLogin() == ValidationStatus.VALID) { AfterLogin(null); } } ProfileName.Content = App.Profile; URL.Content = GetDatabaseConnectionDescription(); if (loginStatus == ValidationStatus.VALID && DatabaseType == DatabaseType.Standalone) { Progress.ShowModal("Starting Scheduler", progress => { scheduler.Start(); }); } } #region Connection Management private string GetDatabaseConnectionDescription() { return DatabaseType switch { #if RPC DatabaseType.Networked => (ClientFactory.Parameters?.FirstOrDefault() as RpcClientSocketTransport)?.Host, #else DatabaseType.Networked => ClientFactory.Parameters?.FirstOrDefault() as string, #endif DatabaseType.Standalone => App.DatabaseSettings?.FileName, DatabaseType.Local => App.DatabaseSettings?.LocalServerName, _ => "" } ?? ""; } /// /// Reconnect to the server. /// /// if connection was successful. private bool Reconnect() { if (_transport != null) { return _transport.Connect(); } Logger.Send(LogType.Error, ClientFactory.UserID, "Trying to reconnect without a transport set."); return true; // Returning true so we don't get stuck in infinite loops in exceptional circumstances. } private enum ConnectionFailedResult { Quit, Restart, Ok } /// /// To be called when initial connection to the server has failed; asks the user if they want to retry, /// change the database settings, or simply quit PRS. /// /// The action to take next. /// private ConnectionFailedResult DoConnectionFailed() { bool connected = false; while (!connected) { var connectionFailedWindow = new ConnectionFailed(); connectionFailedWindow.ShowDialog(); var reconnect = false; switch (connectionFailedWindow.Result) { case ConnectionFailedWindowResult.OpenDatabaseConfiguration: var result = ShowDatabaseConfiguration(); switch (result) { case DatabaseConfigurationResult.RestartRequired: var shouldRestart = MessageBox.Show( "A restart is required to apply these changes. Do you wish to restart now?", "Restart?", MessageBoxButton.YesNo); if (shouldRestart == MessageBoxResult.Yes) { return ConnectionFailedResult.Restart; } else { reconnect = true; } break; case DatabaseConfigurationResult.RestartNotRequired: reconnect = true; break; case DatabaseConfigurationResult.Cancel: reconnect = true; break; default: throw new Exception($"Invalid enum result {result}"); } break; case ConnectionFailedWindowResult.RetryConnection: reconnect = true; break; case ConnectionFailedWindowResult.Quit: return ConnectionFailedResult.Quit; default: throw new Exception($"Invalid enum result {connectionFailedWindow.Result}"); } if (!reconnect) { return ConnectionFailedResult.Quit; } connected = Reconnect(); } return ConnectionFailedResult.Ok; } private void Transport_OnOpen(IRpcTransport transport, RpcTransportOpenArgs e) { Logger.Send(LogType.Information, ClientFactory.UserID, "Connection opened"); } private void Transport_OnException(IRpcTransport transport, RpcTransportExceptionArgs e) { Logger.Send(LogType.Error, ClientFactory.UserID, $"Error in connection: {CoreUtils.FormatException(e.Exception)}"); } private void TransportConnectionLost(IRpcTransport transport, RpcTransportCloseArgs e) { Logger.Send(LogType.Information, ClientFactory.UserID, "Connection lost"); if (transport is IRpcClientTransport client) { Dispatcher.Invoke(() => { var reconnection = new ReconnectionWindow(); var cancellationTokenSource = new CancellationTokenSource(); reconnection.OnCancelled = () => cancellationTokenSource.Cancel(); var ct = cancellationTokenSource.Token; var work = () => { try { DateTime lost = DateTime.Now; while (!client.IsConnected() && !ct.IsCancellationRequested) { try { Logger.Send(LogType.Error, ClientFactory.UserID, $"Reconnecting - ({DateTime.Now - lost:hh\\:mm})"); if (client.Connect(ct)) { break; } } catch (System.Exception e1) { Logger.Send(LogType.Error, ClientFactory.UserID, $"Reconnect Failed: {e1.Message}"); // TODO: Remove this suppression if (e1.Message.StartsWith("The socket is connected, you needn't connect again!")) { break; } } } if (client.IsConnected()) { Logger.Send(LogType.Information, ClientFactory.UserID, "Reconnected"); ClientFactory.Validate(ClientFactory.SessionID); Logger.Send(LogType.Information, ClientFactory.UserID, "Validated"); return true; } } catch (Exception e) { Logger.Send(LogType.Error, ClientFactory.UserID, $"Reconnect Failed: {e.Message}"); } return false; }; var task = Task.Run(() => { var result = work(); Dispatcher.Invoke(() => { reconnection.Close(); }); return result; }, ct); reconnection.ShowDialog(); if (!task.Result) { Close(); } }); } } #endregion private bool _loggingOut = false; private void ClientFactory_OnRequestError(RequestException e) { if (e.Status == StatusCode.Unauthenticated) { switch (e.Method) { case RequestMethod.Query: case RequestMethod.Save: case RequestMethod.Delete: case RequestMethod.MultiQuery: case RequestMethod.MultiSave: case RequestMethod.MultiDelete: if (!_loggingOut) { Dispatcher.InvokeAsync(() => { _loggingOut = true; try { Logout(null, true); } finally { _loggingOut = false; } }); } break; default: break; } } } private void ApplyColorScheme() { Color baseColor; try { baseColor = (Color)ColorConverter.ConvertFromString(App.DatabaseSettings.ColorScheme); } catch { baseColor = Colors.CornflowerBlue; } ThemeManager.BaseColor = baseColor; DynamicGridUtils.SelectionBackground = ThemeManager.SelectionBackgroundBrush; DynamicGridUtils.SelectionForeground = ThemeManager.SelectionForegroundBrush; DynamicGridUtils.FilterBackground = ThemeManager.FilterBackgroundBrush; //_ribbon.Background = new SolidColorBrush(Colors.White); //_ribbon.BackStageColor = ThemeConverter.GetBrush(ElementType.Ribbon, BrushType.Background); ////_ribbon.BackStage.Background = ThemeConverter.GetBrush(ElementType.Ribbon, BrushType.Background); ////_ribbon.BackStage.Foreground = ThemeConverter.GetBrush(ElementType.Ribbon, BrushType.Foreground); UpdateRibbonColors(); PanelHost.Refresh(); } #region Configuration /* protected override void OnSourceInitialized(EventArgs e) { base.OnSourceInitialized(e); var source = PresentationSource.FromVisual(this) as HwndSource; source?.AddHook(WndProc); } private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) { var message = (App.Message)msg; switch (message) { case App.Message.Maximise: WindowState = WindowState.Maximized; break; } return IntPtr.Zero; }*/ private void ConfigureMainScreen(IProgress? progress) { var bMaps = Security.CanView() || Security.CanView() || Security.CanView() || Security.CanView(); var sections = new[] { new ProgressSection("Configuring Main Screen", SetupMainScreen), new ProgressSection("Configuring Projects", () => SetupProjectsTab(bMaps)), new ProgressSection("Configuring Manufacturing", () => SetupManufacturingTab(bMaps)), new ProgressSection("Configuring Logistics", () => SetupLogisticsTab(bMaps)), new ProgressSection("Configuring Products", () => SetupProductsTab(bMaps)), new ProgressSection("Configuring Human Resources", () => SetupHumanResourcesTab(bMaps)), new ProgressSection("Configuring Accounts", () => SetupAccountsTab(bMaps)), new ProgressSection("Configuring Equipment", () => SetupEquipmentTab(bMaps)), new ProgressSection("Configuring DigitalForms", () => SetupDigitalFormsTab(bMaps)), new ProgressSection("Configuring Dashboards", () => SetupDashboardsTab(bMaps)), new ProgressSection("Configuring System Modules", SetupSystemModules) }; if(progress is not null) { Dispatcher.Invoke(SetupScreen); foreach(var section in sections) { progress.Report(section.Message); Dispatcher.Invoke(section.Action); } } else { SetupScreen(); Progress.ShowModal(sections); } } private void SetupScreen() { var button = _ribbon.FindVisualChildren().FirstOrDefault(); if (button != null) button.Visibility = Visibility.Collapsed; if (ClientFactory.UserGuid == Guid.Empty) _ribbonRow.Height = new GridLength(30, GridUnitType.Pixel); else _ribbonRow.Height = new GridLength(1, GridUnitType.Auto); } private void SetupMainScreen() { //DockManager.SidePanelSize = OutstandingDailyReports(false) ? 0.00F : 30.00F; // Notifications Area SetVisibility(SendNotification, Security.CanView()); SetVisibility(Notifications, Security.CanView()); SetVisibility(TaskTracking, Security.IsAllowed()); UserID.Content = ClientFactory.UserID; if (ClientFactory.PasswordExpiration != DateTime.MinValue) { var timeUntilExpiration = ClientFactory.PasswordExpiration - DateTime.Now; if (timeUntilExpiration.Days < 14) { PasswordExpiryNotice.Content = $"Password will expire in {timeUntilExpiration.Days} days!"; PasswordExpiryNotice.Visibility = Visibility.Visible; } else { PasswordExpiryNotice.Visibility = Visibility.Collapsed; } } } private void SetupSystemModules() { SetVisibility(CompanyInformation, Security.CanView()); SetVisibleIfAny(BackstageSeparator0, CompanyInformation); SetVisibility(SecurityDefaultsButton, ClientFactory.IsSupported() && Security.IsAllowed()); SetVisibleIfAny(BackstageSeparator1, SecurityDefaultsButton); BackstageSeparator1a.Visibility = Visibility.Visible; SystemLogsButton.Visibility = Visibility.Visible; SetVisibility(DocumentTypeList, ClientFactory.IsSupported() && Security.IsAllowed()); SetVisibleIfAny(BackstageSeparator2, DocumentTypeList); SetVisibility(VideoRecordingButton, Security.IsAllowed()); LogoutButton.Visibility = ClientFactory.UserGuid == Guid.Empty ? Visibility.Collapsed : Visibility.Visible; LoginButton.Visibility = ClientFactory.UserGuid != Guid.Empty ? Visibility.Collapsed : Visibility.Visible; EditDetailsButton.Visibility = ClientFactory.UserGuid == Guid.Empty ? Visibility.Collapsed : Visibility.Visible; SetupDock(ContactDock, Contacts); SetupDock(JobDock, Jobs); SetupDock(ConsignmentDock, Consignments); SetupDock(DeliveryDock, Deliveries); SetupDock(ProductLookupDock, ProductLookup); SetupDock(DigitalFormsDock, DigitalForms); SetupDock(RequisitionsDock, Requisitions); _ribbon.InvalidateArrange(); } private void SetupDashboardsTab(bool bMaps) { if (!Security.IsAllowed()) return; SetVisibility(DashboardsDashboardButton, Security.IsAllowed()); SetVisibility(DashboardMessagesButton, Security.CanView()); SetVisibility(DashboardsTaskButton, ClientFactory.IsSupported() && Security.IsAllowed()); SetVisibility(DashboardsAttendanceButton, ClientFactory.IsSupported() && Security.IsAllowed()); SetVisibility(DashboardsMapButton, bMaps); SetVisibility(DashboardsDailyReportButton, ClientFactory.IsSupported() && Security.IsAllowed()); SetVisibility(FactoryProductivityButton, ClientFactory.IsSupported() && Security.IsAllowed() && Security.IsAllowed()); SetVisibility(TemplateAnalysisButton, ClientFactory.IsSupported() && Security.IsAllowed() && Security.IsAllowed()); SetVisibility(FactoryAnalysisButton, ClientFactory.IsSupported() && Security.IsAllowed() && Security.IsAllowed()); SetVisibility(DatabaseActivityButton, ClientFactory.IsSupported() && Security.IsAllowed() && Security.IsAllowed()); SetVisibility(UserActivityButton, ClientFactory.IsSupported() && Security.IsAllowed() && Security.IsAllowed()); SetVisibility(QuickStatusButton, Security.IsAllowed() && Security.IsAllowed()); SetVisibleIfEither(DashboardsTaskSeparator, new FrameworkElement[] { DashboardsDashboardButton, DashboardMessagesButton, DashboardsTaskButton, DashboardsAttendanceButton, DashboardsMapButton, DashboardsDailyReportButton }, new FrameworkElement[] { FactoryProductivityButton, TemplateAnalysisButton, FactoryAnalysisButton, DatabaseActivityButton, UserActivityButton, QuickStatusButton }); SetVisibleIfAny(DashboardsActions, DashboardsDashboardButton, DashboardMessagesButton, DashboardsTaskButton, DashboardsAttendanceButton, DashboardsDailyReportButton, FactoryProductivityButton, TemplateAnalysisButton, FactoryAnalysisButton, DatabaseActivityButton, UserActivityButton, QuickStatusButton); //DashboardsActions.IsLauncherButtonVisible = Security.IsAllowed(); //DashboardsReports.IsLauncherButtonVisible = Security.IsAllowed(); SetVisibleIfAny(DashboardsTab, FactoryProductivityButton, TemplateAnalysisButton, FactoryAnalysisButton, DatabaseActivityButton, UserActivityButton, QuickStatusButton); } private void SetupDigitalFormsTab(bool bMaps) { if (!Security.IsAllowed()) return; SetVisibility(DigitalFormsDashboardButton, Security.IsAllowed()); SetVisibility(DigitalFormsMessagesButton, Security.CanView()); SetVisibility(DigitalFormsTaskButton, ClientFactory.IsSupported() && Security.IsAllowed()); SetVisibility(DigitalFormsAttendanceButton, ClientFactory.IsSupported() && Security.IsAllowed()); SetVisibility(DigitalFormsMapButton, bMaps); SetVisibility(DigitalFormsDailyReportButton, ClientFactory.IsSupported() && Security.IsAllowed()); SetVisibility(DigitalFormsFormsLibraryButton, ClientFactory.IsSupported() && Security.CanView() && Security.IsAllowed()); SetVisibility(DigitalFormsCompletedFormsButton, Security.IsAllowed() && Security.IsAllowed()); SetVisibleIfEither(DigitalFormsTaskSeparator, new FrameworkElement[] { DigitalFormsDashboardButton, DigitalFormsMessagesButton, DigitalFormsTaskButton, DigitalFormsAttendanceButton, DigitalFormsMapButton, DigitalFormsDailyReportButton }, new FrameworkElement[] { DigitalFormsFormsLibraryButton, DigitalFormsCompletedFormsButton }); SetVisibleIfAny(DigitalFormsActions, DigitalFormsDashboardButton, DigitalFormsMessagesButton, DigitalFormsTaskButton, DigitalFormsAttendanceButton, DigitalFormsDailyReportButton, DigitalFormsFormsLibraryButton, DigitalFormsCompletedFormsButton); SetTabVisibleIfAny(DigitalFormsTab, DigitalFormsFormsLibraryButton, DigitalFormsCompletedFormsButton); } private void SetupEquipmentTab(bool bMaps) { if (!Security.IsAllowed()) return; SetVisibility(EquipmentDashboardButton, Security.IsAllowed()); SetVisibility(EquipmentMessagesButton, Security.CanView()); SetVisibility(EquipmentTaskButton, ClientFactory.IsSupported() && Security.IsAllowed()); SetVisibility(EquipmentAttendanceButton, ClientFactory.IsSupported() && Security.IsAllowed()); SetVisibility(EquipmentMapButton, bMaps); SetVisibility(EquipmentDailyReportButton, ClientFactory.IsSupported() && Security.IsAllowed()); SetVisibility(EquipmentButton, ClientFactory.IsSupported() && Security.CanView() && Security.IsAllowed()); SetVisibility(EquipmentPlannerButton, ClientFactory.IsSupported() && Security.CanView() && ClientFactory.IsSupported() && Security.CanView() && Security.IsAllowed()); SetVisibleIfEither(EquipmentTaskSeparator, new FrameworkElement[] { EquipmentDashboardButton, EquipmentMessagesButton, EquipmentTaskButton, EquipmentAttendanceButton, EquipmentMapButton, EquipmentDailyReportButton }, new FrameworkElement[] { EquipmentButton, EquipmentPlannerButton }); SetVisibleIfAny(EquipmentActions, EquipmentDashboardButton, EquipmentMessagesButton, EquipmentTaskButton, EquipmentAttendanceButton, EquipmentDailyReportButton, EquipmentButton, EquipmentPlannerButton); SetVisibility(TrackersMasterList, Security.CanView() && Security.IsAllowed()); SetTabVisibleIfAny(EquipmentTab, EquipmentButton, TrackersMasterList); } private void SetupAccountsTab(bool bMaps) { if (!Security.IsAllowed()) return; SetVisibility(AccountsDashboardButton, Security.IsAllowed()); SetVisibility(AccountsMessagesButton, Security.CanView()); SetVisibility(AccountsTaskButton, ClientFactory.IsSupported() && Security.IsAllowed()); SetVisibility(AccountsAttendanceButton, ClientFactory.IsSupported() && Security.IsAllowed()); SetVisibility(AccountsMapButton, bMaps); SetVisibility(AccountsDailyReportButton, ClientFactory.IsSupported() && Security.IsAllowed()); SetVisibility(CustomerList, ClientFactory.IsSupported() && Security.CanView() && Security.IsAllowed()); SetVisibility(InvoiceList, ClientFactory.IsSupported() && Security.CanView() && Security.IsAllowed()); SetVisibility(ReceiptList, ClientFactory.IsSupported() && Security.CanView() && Security.IsAllowed()); SetVisibility(SupplierList, ClientFactory.IsSupported() && Security.CanView() && Security.IsAllowed()); SetVisibility(AccountsDataButton, Security.IsAllowed()); SetVisibility(PurchasesList, ClientFactory.IsSupported() && Security.CanView() && Security.IsAllowed()); SetVisibility(BillsList, ClientFactory.IsSupported() && Security.CanView() && Security.IsAllowed()); SetVisibility(PaymentsList, ClientFactory.IsSupported() && Security.CanView() && Security.IsAllowed()); SetVisibleIfEither(AccountsTaskSeparator1, new FrameworkElement[] { AccountsDashboardButton, AccountsMessagesButton, AccountsTaskButton, AccountsAttendanceButton, AccountsMapButton, AccountsDailyReportButton }, new FrameworkElement[] { CustomerList, InvoiceList, ReceiptList }); SetVisibleIfEither(AccountsTaskSeparator2, new FrameworkElement[] { CustomerList, InvoiceList, ReceiptList }, new FrameworkElement[] { SupplierList, AccountsDataButton, PurchasesList, BillsList, PaymentsList }); SetVisibleIfAny(AccountsActions, AccountsDashboardButton, AccountsMessagesButton, AccountsTaskButton, AccountsAttendanceButton, AccountsDailyReportButton, CustomerList, InvoiceList, ReceiptList, SupplierList, PurchasesList, BillsList, PaymentsList); SetTabVisibleIfAny(AccountsTab, CustomerList, InvoiceList, ReceiptList, SupplierList, AccountsDataButton, PurchasesList, BillsList, PaymentsList); } private void SetupHumanResourcesTab(bool bMaps) { if (!Security.IsAllowed()) return; SetVisibility(HumanResourcesDashboardButton, Security.IsAllowed()); SetVisibility(HumanResourcesMessagesButton, Security.CanView()); SetVisibility(HumanResourcesTaskButton, ClientFactory.IsSupported() && Security.IsAllowed()); SetVisibility(HumanResourcesAttendanceButton, ClientFactory.IsSupported() && Security.IsAllowed()); SetVisibility(HumanResourcesMapButton, bMaps); SetVisibility(HumanResourcesDailyReportButton, ClientFactory.IsSupported() && Security.IsAllowed()); SetVisibility(CalendarButton, Security.CanView() && Security.IsAllowed()); SetVisibility(EmployeePlannerButton, Security.CanView() && Security.IsAllowed()); SetVisibility(TimesheetsButton, Security.CanView() && Security.IsAllowed()); SetVisibility(LeaveRequestsButton, Security.CanView() && Security.IsAllowed()); SetVisibility(OrgChartButton, ClientFactory.IsSupported() && Security.IsAllowed() ); SetVisibility(MeetingsButton, Security.IsAllowed()); SetVisibleIfEither(HumanResourcesTaskSeparator, new FrameworkElement[] { HumanResourcesDashboardButton, HumanResourcesMessagesButton, HumanResourcesTaskButton, HumanResourcesAttendanceButton, HumanResourcesMapButton, HumanResourcesDailyReportButton }, new FrameworkElement[] { CalendarButton, EmployeePlannerButton, TimesheetsButton, LeaveRequestsButton, OrgChartButton }); SetVisibleIfAny(HumanResourcesActions, HumanResourcesDashboardButton, HumanResourcesTaskButton, HumanResourcesAttendanceButton, HumanResourcesDailyReportButton, CalendarButton, EmployeePlannerButton, TimesheetsButton, LeaveRequestsButton, OrgChartButton); SetVisibility(UsersButton, Security.CanView() && Security.IsAllowed()); SetVisibility(EmployeesButton, Security.CanView() && Security.IsAllowed()); SetVisibleIfEither(HumanResourcesSetupSeparator1, new FrameworkElement[] { CalendarButton, TimesheetsButton, LeaveRequestsButton, OrgChartButton }, new FrameworkElement[] { UsersButton, EmployeesButton }); SetTabVisibleIfAny(HumanResourcesTab, CalendarButton, TimesheetsButton, LeaveRequestsButton, OrgChartButton, UsersButton, EmployeesButton); } private void SetupProductsTab(bool bMaps) { if (!Security.IsAllowed()) return; SetVisibility(ProductsDashboardButton, Security.IsAllowed()); SetVisibility(ProductsMessagesButton, Security.CanView()); SetVisibility(ProductsTaskButton, Security.CanView()); SetVisibility(ProductsAttendanceButton, Security.IsAllowed()); SetVisibility(ProductsMapButton, bMaps); SetVisibility(ProductsDailyReportButton, ClientFactory.IsSupported() && Security.IsAllowed()); SetVisibility(ProductsMasterList, Security.CanView() && Security.IsAllowed()); SetVisibility(StockLocationList, Security.CanView() && Security.IsAllowed()); SetVisibility(StockMovementList, Security.CanView() && Security.IsAllowed()); SetVisibility(StockForecastButton, Security.CanView() && Security.CanView() && Security.IsAllowed()); SetVisibility(ReservationManagementButton, Security.IsAllowed()); SetVisibleIfEither(ProductsTaskSeparator, new FrameworkElement[] { ProductsDashboardButton, ProductsMessagesButton, ProductsTaskButton, ProductsAttendanceButton, ProductsMapButton, ProductsDailyReportButton }, new FrameworkElement[] { ProductsMasterList, StockLocationList, StockMovementList, StockForecastButton }); SetVisibleIfAny(ProductActions, ProductsMasterList, StockLocationList, StockMovementList, StockForecastButton); SetTabVisibleIfAny(ProductTab, ProductActions); } private void SetupLogisticsTab(bool bMaps) { if (!Security.IsAllowed()) return; SetVisibility(LogisticsDashboardButton, Security.IsAllowed()); SetVisibility(LogisticsMessagesButton, Security.CanView()); SetVisibility(LogisticsTaskButton, ClientFactory.IsSupported() && Security.IsAllowed()); SetVisibility(LogisticsAttendanceButton, ClientFactory.IsSupported() && Security.IsAllowed()); SetVisibility(LogisticsMapButton, bMaps); SetVisibility(LogisticsDailyReportButton, ClientFactory.IsSupported() && Security.IsAllowed()); SetVisibility(ReadyToGoItemsButton, ClientFactory.IsSupported() && Security.IsAllowed() && Security.IsAllowed()); SetVisibility(DispatchButton, Security.CanView() && Security.CanView() && Security.IsAllowed()); SetVisibility(RequisitionsButton, Security.CanView() && Security.IsAllowed()); SetVisibility(DeliveriesButton, Security.IsAllowed() && Security.IsAllowed()); SetVisibility(DeliveredItemsButton, ClientFactory.IsSupported() && Security.IsAllowed() && Security.IsAllowed()); SetVisibility(ConsignmentButton, Security.CanView() && Security.IsAllowed()); SetVisibleIfEither(LogisticsTaskSeparator1, new FrameworkElement[] { LogisticsDashboardButton, LogisticsMessagesButton, LogisticsTaskButton, LogisticsAttendanceButton, LogisticsMapButton, LogisticsDailyReportButton }, new FrameworkElement[] { ReadyToGoItemsButton, DispatchButton, RequisitionsButton, DeliveriesButton, DeliveredItemsButton }); SetVisibleIfEither(LogisticsTaskSeparator2, new FrameworkElement[] { ReadyToGoItemsButton, DispatchButton, RequisitionsButton, DeliveriesButton, DeliveredItemsButton }, new FrameworkElement[] { ConsignmentButton }); SetTabVisibleIfAny(LogisticsTab, DispatchButton, RequisitionsButton, DeliveriesButton, ReadyToGoItemsButton, DeliveredItemsButton, ConsignmentButton); } private void SetupManufacturingTab(bool bMaps) { if (!Security.IsAllowed()) return; SetVisibility(ManufacturingDashboardButton, Security.IsAllowed()); SetVisibility(ManufacturingMessagesButton, Security.CanView()); SetVisibility(ManufacturingTaskButton, ClientFactory.IsSupported() && Security.IsAllowed()); SetVisibility(ManufacturingAttendanceButton, ClientFactory.IsSupported() && Security.IsAllowed()); SetVisibility(ManufacturingMapButton, bMaps); SetVisibility(ManufacturingDailyReportButton, ClientFactory.IsSupported() && Security.IsAllowed()); SetVisibleIfAny(ManufacturingTaskSeparator, new FrameworkElement[] { ManufacturingDashboardButton, ManufacturingMessagesButton, ManufacturingTaskButton, ManufacturingAttendanceButton, ManufacturingMapButton, ManufacturingDailyReportButton }); SetVisibility(DesignManagementButton, Security.CanView() && Security.IsAllowed()); SetVisibility(ManufacturingDesignSeparator, Security.CanView() && Security.IsAllowed()); SetVisibility(FactoryStatusButton, ClientFactory.IsSupported() && Security.IsAllowed() && Security.IsAllowed()); SetVisibility(FactoryAllocationButton, ClientFactory.IsSupported() && Security.IsAllowed() && Security.IsAllowed()); //SetVisibility(FactoryScheduleButton, ClientFactory.IsSupported() && ClientFactory.IsEnabled<>()); SetVisibility(FactoryFloorButton, ClientFactory.IsSupported() && Security.IsAllowed() && Security.IsAllowed()); //SetVisibility(FactoryReadyButton, ClientFactory.IsSupported() && Security.IsAllowed()); SetVisibleIfAny(ManufacturingActionSeparator, new FrameworkElement[] { FactoryStatusButton, FactoryAllocationButton, FactoryFloorButton }); SetVisibleIfAny(ManufacturingActions, ManufacturingDashboardButton, ManufacturingMessagesButton, ManufacturingTaskButton, ManufacturingAttendanceButton, ManufacturingDailyReportButton, FactoryStatusButton, FactoryAllocationButton, FactoryFloorButton); SetTabVisibleIfAny(ManufacturingTab, FactoryStatusButton, FactoryAllocationButton, FactoryFloorButton); } private void SetupProjectsTab(bool bMaps) { if (!Security.IsAllowed()) return; SetVisibility(ProjectsDashboardButton, Security.IsAllowed()); SetVisibility(ProjectMessagesButton, Security.CanView()); SetVisibility(ProjectTaskButton, ClientFactory.IsSupported() && Security.IsAllowed()); SetVisibility(ProjectAttendanceButton, ClientFactory.IsSupported() && Security.IsAllowed()); SetVisibility(ProjectsMapButton, bMaps); SetVisibility(ProjectDailyReportButton, ClientFactory.IsSupported() && Security.IsAllowed()); SetVisibility(ProjectsButton, Security.CanView() && Security.IsAllowed()); //SetVisibility(ServiceButton, Security.CanView() && Security.IsAllowed()); SetVisibility(ProjectPlannerButton, Security.CanView() && Security.IsAllowed()); SetVisibleIfEither(ProjectTaskSeparator, new FrameworkElement[] { ProjectsDashboardButton, ProjectMessagesButton, ProjectTaskButton, ProjectAttendanceButton, ProjectsMapButton, ProjectDailyReportButton }, new FrameworkElement[] { QuotesButton, ProjectsButton, ProjectPlannerButton }); SetVisibility(QuotesButton, Security.CanView() && Security.IsAllowed()); SetVisibility(KitsMasterList, Security.CanView() && Security.IsAllowed()); SetVisibility(CostSheetsMasterList, Security.CanView() && Security.IsAllowed()); SetVisibleIfAny(ProjectsSetup, KitsMasterList, CostSheetsMasterList); //ProjectsActions.IsLauncherButtonVisible = Security.IsAllowed(); //ProjectReports.IsLauncherButtonVisible = Security.IsAllowed(); SetTabVisibleIfAny(ProjectsTab, QuotesButton, ProjectsButton, ProjectPlannerButton, CostSheetsMasterList, KitsMasterList); } private void SetupDock(LayoutAnchorable layout, IDockPanel dock) where TSecurityDescriptor : ISecurityDescriptor, new() { if (Security.IsAllowed()) { if (!DockGroup.Children.Any(x => x == layout)) { DockGroup.Children.Add(layout); } if (layout.IsVisible && (ClientFactory.UserGuid != Guid.Empty)) dock.Setup(); } else { DockGroup.RemoveChild(layout); } } private void LoadApplicationState() { if (ClientFactory.UserGuid != Guid.Empty) { _ribbon.IsCollapsed = false; if (OutstandingDailyReports(false)) { MessageWindow.ShowMessage("There are outstanding Daily Reports that must be filled out before continuing!" + "\n\nAccess to PRS is restricted until this is corrected.", "Outstanding Reports"); var dailyReportPanel = LoadWindow(ProjectDailyReportButton); if(dailyReportPanel is not null) { dailyReportPanel.OnTimeSheetConfirmed += e => { if (!OutstandingDailyReports(true)) { ConfigureMainScreen(null); LoadApplicationState(); } }; } return; } using (new WaitCursor()) { _ribbon.IsCollapsed = false; LoadInitialWindow(); } } } private void LoadInitialWindow() { var app = new LocalConfiguration().Load(); if (app.Settings.ContainsKey("CurrentPanel")) { try { var bFound = false; var module = app.Settings["CurrentPanel"].Split(new[] { " / " }, StringSplitOptions.None); if (module.Length == 2) foreach (Fluent.RibbonTabItem tab in _ribbon.Tabs) { if (String.Equals(tab.Header, module.First())) { _ribbon.SelectedTabItem = tab; foreach (Fluent.RibbonGroupBox bar in tab.Groups) { foreach (var item in bar.Items) { var button = item as Fluent.Button; if (button != null && String.Equals(button.Header, module.Last())) { bFound = true; button.RaiseEvent(new RoutedEventArgs(ButtonBase.ClickEvent)); break; } } if (bFound) break; } } if (bFound) break; } } catch (Exception e) { MessageWindow.ShowError($"Unable to load {app.Settings["CurrentPanel"]}", e); } } } private void _ribbon_OnLoaded(object sender, RoutedEventArgs e) { _ribbon.SelectedTabItem = CurrentTab; } private void LoadSecondaryWindows() { if (ClientFactory.UserGuid != Guid.Empty) { var windows = App.DatabaseSettings.SecondaryWindows; foreach (var key in windows.Keys.ToArray()) { SecondaryWindows[key] = new SecondaryWindow( key, windows[key].Item1, windows[key].Item2, windows[key].Item3, windows[key].Item4, windows[key].Item5, windows[key].Item6 ); SecondaryWindows[key].Closed += (o, e) => { SecondaryWindows.Remove(key); }; SecondaryWindows[key].Show(); } } else { foreach (var key in SecondaryWindows.Keys.ToArray()) { App.IsClosing = true; SecondaryWindows[key].Close(); App.IsClosing = false; } } } private Fluent.RibbonTabItem GetTabItem(FrameworkElement? sender) { if (sender == null) throw new Exception("No Tab Found!"); if (sender is Fluent.RibbonTabItem) return (Fluent.RibbonTabItem)sender; return GetTabItem(sender.Parent as FrameworkElement); } private IEnumerable Panels { get { if(PanelHost.CurrentPanel is not null) { yield return PanelHost.CurrentPanel; } foreach(var window in SecondaryWindows.Values) { yield return window.Panel; } } } private T? LoadWindow(Fluent.Button sender) where T : class, IBasePanel, new() { return LoadWindow(sender, new CancelEventArgs()); } private T? LoadWindow(Fluent.Button sender, CancelEventArgs cancel) where T : class, IBasePanel, new() { using (new WaitCursor()) { UnloadWindow(cancel); if (cancel.Cancel) { return null; } CurrentTab = GetTabItem(sender); CurrentButton = sender; //CurrentButton.IsSelected = true; UpdateRibbonColors(); var moduleName = sender.Header?.ToString() ?? ""; var panel = PanelHost.LoadPanel(moduleName); ContentControl.Content = panel; Title = $"{moduleName} - {(String.Equals(App.Profile?.ToUpper(), "DEFAULT") ? "PRS Desktop" : App.Profile)} (Release {CoreUtils.GetVersion()})"; PanelHost.Refresh(); if (panel is NotificationPanel) { Logger.Send(LogType.Information, ClientFactory.UserID, "Disabling Heartbeat"); NotificationsWatchDog.IsEnabled = false; Notifications.Visibility = Visibility.Collapsed; DockingGrid.ColumnDefinitions[1].Width = new GridLength(0, GridUnitType.Pixel); DockingGrid.ColumnDefinitions[2].Width = new GridLength(0, GridUnitType.Pixel); } else { ReloadNotifications(); } if (sender != null) { var settings = new LocalConfiguration().Load(); var module = string.Format("{0} / {1}", CurrentTab?.Header, sender.Header); if (!settings.Settings.ContainsKey("CurrentPanel") || module != settings.Settings["CurrentPanel"]) { settings.Settings["CurrentPanel"] = module; try { new LocalConfiguration().Save(settings); } catch (Exception e) { Logger.Send(LogType.Error, "", string.Format("*** Unknown Error: {0}\n{1}", e.Message, e.StackTrace)); } } } return panel; } } private void UpdateRibbonColors() { foreach (var tab in _ribbon.Tabs) { bool bFound = false; foreach (var grp in tab.Groups) { foreach (var btn in grp.Items) { if (btn is Fluent.Button fluentbutton) { bFound = bFound || (btn == CurrentButton); fluentbutton.Background = (btn == CurrentButton) ? ThemeManager.SelectedTabItemBackgroundBrush : new SolidColorBrush(Colors.White); fluentbutton.Foreground = (btn == CurrentButton) ? ThemeManager.SelectedTabItemForegroundBrush : new SolidColorBrush(Colors.Black); } } tab.Background = bFound ? ThemeManager.SelectedTabItemBackgroundBrush : new SolidColorBrush(Colors.White); tab.Foreground = bFound ? ThemeManager.SelectedTabItemForegroundBrush : new SolidColorBrush(Colors.Black); } } } private static void StartLocalDatabase(IProgress progress) { var dirName = Path.GetDirectoryName(App.DatabaseSettings.FileName); if (!Directory.Exists(dirName) && dirName != null) Directory.CreateDirectory(dirName); var FileName = App.DatabaseSettings.FileName; var Exists = File.Exists(FileName); progress.Report("Configuring Stores"); DbFactory.Stores = CoreUtils.TypeList( new[] { typeof(Store<>).Assembly, typeof(EquipmentStore).Assembly }, myType => myType.IsClass && !myType.IsAbstract && !myType.IsGenericType && myType.GetInterfaces().Contains(typeof(IStore)) ).ToArray(); DbFactory.ProviderFactory = new SQLiteProviderFactory(App.DatabaseSettings.FileName); DbFactory.ColorScheme = App.DatabaseSettings.ColorScheme; DbFactory.Logo = App.DatabaseSettings.Logo; progress.Report("Starting Local Database"); DbFactory.Start(); ClientFactory.DatabaseID = DbFactory.ID; progress.Report("Checking Database"); var users = DbFactory.NewProvider(Logger.Main).Load(); if (!users.Any()) { var user = new User { UserID = "ADMIN", Password = "admin" }; DbFactory.NewProvider(Logger.Main).Save(user); var employee = DbFactory.NewProvider(Logger.Main).Load(new Filter(x => x.Code).IsEqualTo("ADMIN")).FirstOrDefault(); employee ??= new Employee { Code = "ADMIN", Name = "Administrator Account" }; employee.UserLink.ID = user.ID; DbFactory.NewProvider(Logger.Main).Save(employee); } StoreUtils.GoogleAPIKey = App.DatabaseSettings.GoogleAPIKey; Job.JobNumberPrefix = App.DatabaseSettings.JobPrefix; PurchaseOrder.PONumberPrefix = App.DatabaseSettings.PurchaseOrderPrefix; } #endregion #region Login Management private ValidationStatus? TryAutoLogin() { ValidationStatus? result = null; if (App.DatabaseSettings.LoginType == LoginType.UserID) { try { result = ClientFactory.Validate(App.DatabaseSettings.UserID, App.DatabaseSettings.Password); } catch (Exception e) { MessageWindow.ShowError("Error connecting to server.\nPlease check the server URL and port number.", e); result = null; } if (result == ValidationStatus.INVALID) { MessageWindow.ShowMessage("Unable to Login with User ID: " + App.DatabaseSettings.UserID, "Login failed"); } } return result; } private ValidationStatus? DoLogin() { ValidationStatus? result = null; if (result != ValidationStatus.VALID) { var login = new PinLogin(CoreUtils.GetVersion(), result ?? ValidationStatus.INVALID); if (login.ShowDialog() == true) { result = ValidationStatus.VALID; } } return result ?? ValidationStatus.INVALID; } /// /// To be called after and if a valid login session exists. Configures the main screen and loads the windows. /// /// If not , then rather than opening a new progress window, just uses that. private void AfterLogin(IProgress? progress) { Logger.Send(LogType.Information, "", "Loading employee"); LoadCurrentEmployee(); if (CheckTimesheetBypass(true)) { UpdateCurrentLogin(); } else { MessageWindow.ShowMessage("You must clock on before logging in to PRS!", "Not clocked in."); ClientFactory.InvalidateUser(); App.EmployeeID = Guid.Empty; App.EmployeeName = ""; App.EmployeeCode = ""; App.EmployeeEmail = ""; } Logger.Send(LogType.Information, "", "Setting colours"); if(progress is null) { ApplyColorScheme(); } else { Dispatcher.Invoke(ApplyColorScheme); } Logger.Send(LogType.Information, "", "Configuring main window"); ConfigureMainScreen(progress); Logger.Send(LogType.Information, "", "Loading current window"); if(progress is null) { LoadApplicationState(); } else { Dispatcher.Invoke(LoadApplicationState); } Logger.Send(LogType.Information, "", "Loading secondary window"); if(progress is null) { LoadSecondaryWindows(); } else { Dispatcher.Invoke(LoadSecondaryWindows); } //if (_ribbon.Menu.IsVisible) //{ // _ribbon.; //} } /// /// Creates a new if one does not already exist. Otherwise, updates the entry in the database with new Station ID. /// private void UpdateCurrentLogin() { if (CoreUtils.GetVersion().Equals("???")) return; // Register this station with the Server // Later on, the heartbeat will check to make sure // that the StationID hasn't changed. If it has, // then we've logged in somewhere else and we'll // drop out of this station var curr = new Client().Query( new Filter(x => x.User.ID).IsEqualTo(ClientFactory.UserGuid), null ).Rows.FirstOrDefault(); if (curr != null) { var c = curr.ToObject(); c.StationID = station.StationID; station = c; } else { station.User.ID = ClientFactory.UserGuid; station.TimeStamp = DateTime.Now; } new Client().Save(station, "", (o, e) => { }); } private void LoadCurrentEmployee() { var me = new Client().Query( new Filter(x => x.UserLink.ID).IsEqualTo(ClientFactory.UserGuid), Columns.None().Add(x => x.ID).Add(x => x.Email).Add(x => x.Name) ); App.EmployeeID = me.Rows.FirstOrDefault()?.Get(x => x.ID) ?? Guid.Empty; App.EmployeeName = me.Rows.FirstOrDefault()?.Get(x => x.Name) ?? ""; App.EmployeeCode = me.Rows.FirstOrDefault()?.Get(x => x.Code) ?? ""; App.EmployeeEmail = me.Rows.FirstOrDefault()?.Get(x => x.Email) ?? ""; } private void ExecuteLogout() { new Client().Delete(station, ""); station.ID = Guid.Empty; App.EmployeeID = Guid.Empty; App.EmployeeName = ""; App.EmployeeEmail = ""; } /// /// Logs the user out and unloads windows /// /// A message to display as the reason for logging out, or null for no message. private bool Logout(string? message = null, bool force = false) { // I really don't like all these try-catch blocks; unfortunately, if we are trying to log out and invalidate due to an unauthenticated user, // all the queries that get called here will throw exceptions and thus break our system, failing to log out. try { FinalizeAutoTimesheet(); } catch { if (!force) throw; } // Try to unload the window; try { UnloadWindow(null); if (DatabaseType == DatabaseType.Standalone && !CoreUtils.GetVersion().Equals("???")) scheduler.Stop(); } catch { if (!force) throw; } // Next, try to set things to being empty try { if (!CoreUtils.GetVersion().Equals("???")) if (station.ID != Guid.Empty) { ExecuteLogout(); } ClearTrackingKanban(); } catch { if (!force) throw; } ClientFactory.InvalidateUser(); ConfigureMainScreen(null); LoadSecondaryWindows(); if (message != null) { MessageWindow.ShowMessage(message, "Logged out"); } if (DoLogin() == ValidationStatus.VALID) { AfterLogin(null); } return true; } #endregion #region Timesheets private void RefreshTimeSheets() { if (App.EmployeeID == Guid.Empty) return; var filter = new Filter(x => x.EmployeeLink.ID).IsEqualTo(App.EmployeeID); filter = filter.And(new Filter(x => x.Confirmed).IsEqualTo(DateTime.MinValue).Or(x => x.Date).IsEqualTo(DateTime.Today)); _timesheets = new Client().Query( filter, Columns.None().Add( x => x.ID, x => x.Date, x => x.Finish ) ); } private CoreTable GetTimesheet() { return new Client().Query( new Filter(x => x.Date).IsEqualTo(DateTime.Today) .And(x => x.EmployeeLink.ID).IsEqualTo(App.EmployeeID) .And(x => x.Finish).IsEqualTo(TimeSpan.Zero) ); } private bool CheckTimesheetBypass(bool message) { if (!ClientFactory.IsSupported()) return true; var isClockedOn = IsClockedOn(); if (!Security.IsAllowed()) { if (!isClockedOn) { if (message) MessageBox.Show("You must clock on before opening this screen"); return false; } return true; } if (Security.IsAllowed()) if (!isClockedOn) { var ts = new TimeSheet(); ts.Date = DateTime.Today; ts.Start = DateTime.Now.TimeOfDay; ts.EmployeeLink.ID = App.EmployeeID; ts.Notes = "Automatic Login from PRS Desktop"; new Client().Save(ts, "AutoLogon because Timebench Bypass is enabled", (o, e) => { }); } return true; } private bool IsClockedOn() { RefreshTimeSheets(); if (_timesheets == null) return false; return _timesheets.Rows.Any(r => r.Get(c => c.Date).Date == DateTime.Today && r.Get(c => c.Finish) == new TimeSpan()); } private void FinalizeAutoTimesheet() { if (Security.IsAllowed()) { var check = new Client().Query( new Filter(x => x.Date).IsEqualTo(DateTime.Today).And(x => x.EmployeeLink.ID).IsEqualTo(App.EmployeeID).And(x => x.Finish) .IsEqualTo(TimeSpan.Zero) ); if (check.Rows.Any()) { var ts = check.Rows.First().ToObject(); if (DateTime.Now.TimeOfDay < ts.Start.Add(new TimeSpan(0, 2, 0))) { new Client().Delete(ts, "Deleting Auto TimeSheet because TimeBench Bypass is enabled", (o, ex) => { }); } else { ts.Finish = DateTime.Now.TimeOfDay; new Client().Save(ts, "Clocking off Auto Timesheet because Timesheet Bypass is enabled", (o, ex) => { }); } } } } #endregion private string CurrentPanelSlug() { var app = new LocalConfiguration().Load(); var module = app.Settings["CurrentPanel"].Split(new[] { " / " }, StringSplitOptions.None); return module.LastOrDefault()?.Replace(" ", "_").Replace("/", "") ?? ""; } private bool ShowHelp() { Process.Start(new ProcessStartInfo("https://prsdigital.com.au/wiki/index.php/" + CurrentPanelSlug()) { UseShellExecute = true }); return true; } private void Wiki_Click(object sender, RoutedEventArgs e) { ShowHelp(); } private void Window_Loaded(object sender, RoutedEventArgs e) { } private void UnloadWindow(CancelEventArgs? cancel) { PanelHost.UnloadPanel(cancel); if (cancel?.Cancel == true) { return; } Title = $" - {(String.Equals(App.Profile?.ToUpper(), "DEFAULT") ? "PRS Desktop" : App.Profile)} (Release {CoreUtils.GetVersion()})"; if (CurrentTab is not null) { var border = VisualUtils.EnumChildrenOfType(CurrentTab, typeof(Border)).LastOrDefault(); if (border != null) { ((Border)border).Background = new SolidColorBrush(Colors.Transparent); ((Border)border).BorderBrush = new SolidColorBrush(Colors.Transparent); } var ReportsBar = FindRibbonBar(CurrentTab, x => x.Header.Equals("Print")); if (ReportsBar is not null) { ReportsBar.Items.Clear(); ReportsBar.Visibility = Visibility.Collapsed; ReportsBar.IsLauncherVisible = false; } var ActionBar = FindRibbonBar(CurrentTab, x => x.Header.Equals("Actions")); if (ActionBar is not null) { ActionBar.IsLauncherVisible = false; foreach (var module in CurrentModules) ActionBar.Items.Remove(module); } } CurrentTab = null; CurrentButton = null; ContentControl.Content = null; } private void SecondaryWindow_Click(object sender, RoutedEventArgs e) { var panel = PanelHost.CurrentPanel; if (panel is null) return; var id = Guid.NewGuid(); var window = new Tuple( panel.GetType().EntityName(), PanelHost.CurrentModuleName, Left + 100, Top + 100, Width - 200, Height - 200 ); App.DatabaseSettings.SecondaryWindows[id] = window; new LocalConfiguration(App.Profile).Save(App.DatabaseSettings); SecondaryWindows[id] = new SecondaryWindow( id, window.Item1, window.Item2, window.Item3, window.Item4, window.Item5, window.Item6 ); SecondaryWindows[id].Show(); } private void RibbonWindow_Activated(object sender, EventArgs e) { } private void RegisterModules(IProgress progress) { foreach (Fluent.RibbonTabItem tab in _ribbon.Tabs) foreach (Fluent.RibbonGroupBox bar in tab.Groups) foreach (var item in bar.Items) Dispatcher.Invoke(() => { var button = item as RibbonButton; if (button != null && button.Label != "Refresh") if (bar.Header.Equals("Actions")) Modules.Register(button.Label); }); } #region Visibility private void SetVisibility(FrameworkElement button, bool visible) { var vResult = true; var eResult = ClientFactory.UserGuid != Guid.Empty && visible; button.Visibility = vResult && eResult ? Visibility.Visible : Visibility.Collapsed; if (button is RibbonButton rb) { CustomModules.Register(rb.Label); rb.IsEnabled = !OutstandingDailyReports(false); } } private static void SetVisibleIfEither(FrameworkElement separator, FrameworkElement[] left, FrameworkElement[] right) { var bLeft = false; foreach (var button in left) bLeft = bLeft || button.Visibility == Visibility.Visible; var bRight = false; foreach (var button in right) bRight = bRight || button.Visibility == Visibility.Visible; separator.Visibility = bLeft && bRight ? Visibility.Visible : Visibility.Collapsed; } private static void SetVisibleIfAny(FrameworkElement separator, params FrameworkElement[] buttons) { var bVisible = false; foreach (var button in buttons) bVisible = bVisible || button.Visibility == Visibility.Visible; separator.Visibility = bVisible ? Visibility.Visible : Visibility.Collapsed; } private static void SetTabVisibleIfAny(Fluent.RibbonTabItem tab, params FrameworkElement[] buttons) { var bVisible = false; foreach (var button in buttons) bVisible = bVisible || button.Visibility == Visibility.Visible; tab.Visibility = bVisible ? Visibility.Visible : Visibility.Collapsed; } #endregion private static Fluent.RibbonGroupBox? FindRibbonBar(Fluent.RibbonTabItem tab, Func predicate) { foreach (var group in tab.Groups) { if (group != null) if (predicate.Invoke(group)) return group; } return null; } #region Button Event Handlers #region Common private void Tasks_Checked(object sender, RoutedEventArgs e) { //LoadWindow((RibbonButton)sender); LoadWindow((Fluent.Button)sender); } private void DataEntry_Click(object sender, RoutedEventArgs e) { LoadWindow((Fluent.Button)sender); } private void Attendance_Checked(object sender, RoutedEventArgs e) { LoadWindow((Fluent.Button)sender); } #endregion #region Quotes private void Quotes_Checked(object sender, RoutedEventArgs e) { LoadWindow((Fluent.Button)sender); } #endregion #region Projects private void Jobs_Checked(object sender, RoutedEventArgs e) { LoadWindow((Fluent.Button)sender); } private void ProjectPlanner_Checked(object sender, RoutedEventArgs e) { LoadWindow((Fluent.Button)sender); } private void DesignManagement_Checked(object sender, RoutedEventArgs e) { LoadWindow((Fluent.Button)sender); } //private void Service_Checked(object sender, RoutedEventArgs e) //{ // LoadWindow((Fluent.Button)sender); //} #endregion #region Manufacturing private void ManufacturingMenu_Checked(object sender, RoutedEventArgs e) { LoadWindow((Fluent.Button)sender); } private void FactoryFloorButton_Click(object sender, RoutedEventArgs e) { LoadWindow((Fluent.Button)sender); } #endregion #region Logistics private void ReadyToGoMenu_Checked(object sender, RoutedEventArgs e) { LoadWindow((Fluent.Button)sender); } private void DispatchMenu_Checked(object sender, RoutedEventArgs e) { LoadWindow((Fluent.Button)sender); } private void Requisitions_Checked(object sender, RoutedEventArgs e) { LoadWindow((Fluent.Button)sender); } #endregion #region Products #endregion #region Human Resources private void Timesheets_Checked(object sender, RoutedEventArgs e) { LoadWindow((Fluent.Button)sender); } #endregion #region Accounts #endregion #region Equipment private void Equipment_Checked(object sender, RoutedEventArgs e) { LoadWindow((Fluent.Button)sender); } private void EquipmentMaintenance_Checked(object sender, RoutedEventArgs e) { LoadWindow((Fluent.Button)sender); } #endregion #region Dashboards #endregion private void Console_Click(object sender, RoutedEventArgs a) { if (_console is null) { _console = new DesktopConsole("Console"); _console.Closing += (o, args) => _console = null; } _console.Show(); } //private void Purchases_Checked(object sender, RoutedEventArgs e) //{ // LoadWindow(PurchasesMenu, new PurchasesPanel()); //} private void Maps_Checked(object sender, RoutedEventArgs e) { LoadWindow((Fluent.Button)sender); } private void RefreshMenu_Click(object sender, RoutedEventArgs e) { PanelHost.Refresh(); } private void UserSetup_Click(object sender, RoutedEventArgs e) { LoadWindow((Fluent.Button)sender); } private void Employees_Click(object sender, RoutedEventArgs e) { LoadWindow((Fluent.Button)sender); } private void Trackers_Click(object sender, RoutedEventArgs e) { LoadWindow((Fluent.Button)sender); } private void DeliveredOnSiteMenu_Checked(object sender, RoutedEventArgs e) { LoadWindow((Fluent.Button)sender); } private void Products_Checked(object sender, RoutedEventArgs e) { LoadWindow((Fluent.Button)sender); } private void StockLocations_Checked(object sender, RoutedEventArgs e) { LoadWindow((Fluent.Button)sender); } private void StockMovements_Checked(object sender, RoutedEventArgs e) { LoadWindow((Fluent.Button)sender); } private void StockForecastButton_Clicked(object sender, RoutedEventArgs e) { LoadWindow((Fluent.Button)sender); } private void ReservationManagementButton_Clicked(object sender, RoutedEventArgs e) { LoadWindow((Fluent.Button)sender); } private void KitsMasterList_Click(object sender, RoutedEventArgs e) { LoadWindow((Fluent.Button)sender); } private void CostSheetsMasterList_Click(object sender, RoutedEventArgs e) { LoadWindow((Fluent.Button)sender); } private void LeaveRequestsButton_Click(object sender, RoutedEventArgs e) { LoadWindow((Fluent.Button)sender); } private void MeetingsButton_Click(object sender, RoutedEventArgs e) { LoadWindow((Fluent.Button)sender); } private void OrgChartButton_Click(object sender, RoutedEventArgs e) { LoadWindow((Fluent.Button)sender); } private void SupplierList_Click(object sender, RoutedEventArgs e) { LoadWindow((Fluent.Button)sender); } private void CustomerList_Click(object sender, RoutedEventArgs e) { LoadWindow((Fluent.Button)sender); } private void CalendarButton_Click(object sender, RoutedEventArgs e) { LoadWindow((Fluent.Button)sender); } private void EmployeePlannerButton_Click(object sender, RoutedEventArgs e) { LoadWindow((Fluent.Button)sender); } private void EquipmentPlannerButton_Click(object sender, RoutedEventArgs e) { LoadWindow((Fluent.Button)sender); } private void InvoiceList_Click(object sender, RoutedEventArgs e) { LoadWindow((Fluent.Button)sender); } private void ReceiptList_Click(object sender, RoutedEventArgs e) { LoadWindow((Fluent.Button)sender); } private void PaymentsList_Click(object sender, RoutedEventArgs e) { LoadWindow((Fluent.Button)sender); } private void BillsList_Click(object sender, RoutedEventArgs e) { LoadWindow((Fluent.Button)sender); } private void PurchasesList_Click(object sender, RoutedEventArgs e) { LoadWindow((Fluent.Button)sender); } private void DeliveriesButton_Click(object sender, RoutedEventArgs e) { LoadWindow((Fluent.Button)sender); } private void FactoryAllocationButton_Click(object sender, RoutedEventArgs e) { LoadWindow((Fluent.Button)sender); } private void Messages_Checked(object sender, RoutedEventArgs e) { LoadWindow((Fluent.Button)sender); } private void DatabaseActivityButton_Click(object sender, RoutedEventArgs e) { LoadWindow((Fluent.Button)sender); } private void FactoryProductivityButton_Click(object sender, RoutedEventArgs e) { LoadWindow((Fluent.Button)sender); } private void TemplateAnalysisButton_Click(object sender, RoutedEventArgs e) { LoadWindow((Fluent.Button)sender); } private void FactoryAnalysisButton_Click(object sender, RoutedEventArgs e) { LoadWindow((Fluent.Button)sender); } private void UserActivityButton_Click(object sender, RoutedEventArgs e) { LoadWindow((Fluent.Button)sender); } private void QuickStatus_Click(object sender, RoutedEventArgs e) { LoadWindow((Fluent.Button)sender); } private void ConsignmentButton_Click(object sender, RoutedEventArgs e) { LoadWindow((Fluent.Button)sender); } //private void NotificationsList_SelectionChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e) //{ // if (NotificationsList.SelectedIndex < 0) // return; // var editors = NotificationsList.FindVisualChildren().ToArray(); // var selected = editors[NotificationsList.SelectedIndex]; // selected.Text = (String)selected.Tag; //NotificationsList.SelectedIndex.ToString(); //} //private void ViewNotification_Click(object sender, System.Windows.Input.MouseButtonEventArgs e) //{ // Notification notification = (sender as Label).Tag as Notification; // NotificationDetails details = new NotificationDetails(notification); // details.ShowDialog(); // ReloadNotifications(); //} private void DailyReport_Checked(object sender, RoutedEventArgs e) { LoadWindow((Fluent.Button)sender); } private void Library_Click(object sender, RoutedEventArgs e) { Process.Start( new ProcessStartInfo { FileName = App.DatabaseSettings.LibraryLocation, UseShellExecute = true, Verb = "open" } ); } private void SendNotificationClick(object sender, RoutedEventArgs e) { var form = new NotificationForm { Description = "" }; if (form.ShowDialog() == true) ReloadNotifications(); } private void CompanyInformation_Click(object sender, RoutedEventArgs e) { var info = new Client().Load().FirstOrDefault(); if (info == null) info = new CompanyInformation(); new DynamicDataGrid().EditItems(new[] { info }); } #region Digital Forms private void DigitalFormsFormsLibraryButton_Click(object sender, RoutedEventArgs e) { LoadWindow((Fluent.Button)sender); } private void DigitalFormsCompletedFormsButton_Click(object sender, RoutedEventArgs e) { LoadWindow((Fluent.Button)sender); } #endregion private void Dashboards_Checked(object sender, RoutedEventArgs e) { LoadWindow((Fluent.Button)sender); } private void Setup_Click(object sender, RoutedEventArgs e) { var tab = _ribbon.SelectedTabItem; if (tab is null) return; var menu = new ContextMenu(); PanelHost.InitialiseSetupMenu(menu); menu.IsOpen = true; } private void StartForm(DigitalForm form) where TEntityForm : EntityForm, new() where TEntity : Entity, new() where TEntityLink : EntityLink, new() { var entityForm = new TEntityForm(); entityForm.Form.ID = form.ID; entityForm.Description = form.Description; var entity = DFUtils.NewEntity(form); if (DynamicFormEditWindow.EditDigitalForm(entityForm, out var dataModel, entity)) { dataModel.Update(null); } } private void Forms_Click(object sender, RoutedEventArgs e) { var select = new MultiSelectDialog( Filter.And( LookupFactory.DefineChildFilter(Array.Empty()), new Filter(x => x.ID).InQuery( new Filter(x => x.Employee.ID).IsEqualTo(App.EmployeeID), x => x.Form.ID)), LookupFactory.DefineChildColumns() .Add(x => x.Description), false); if (select.ShowDialog() == true) { var digitalForm = select.Data().Rows.FirstOrDefault()?.ToObject(); if (digitalForm is not null) { StartForm(digitalForm); } }; } #endregion private bool OutstandingDailyReports(bool refresh) { if (!Security.IsAllowed() || Security.IsAllowed()) return false; if (refresh) RefreshTimeSheets(); if (_timesheets == null) return false; return _timesheets.Rows.Any(r => r.Get(c => c.Date).Date < DateTime.Today); } private void ShutDownTransport() { if (_transport != null) { _transport.OnClose -= TransportConnectionLost; if (_transport.IsConnected()) _transport.Disconnect(); _transport = null; } } private void Window_Unloaded(object sender, RoutedEventArgs e) { ShutDownTransport(); } private void RibbonWindow_Closed(object sender, EventArgs e) { ShutDownTransport(); DisconnectRecorderNotes(); Application.Current.Shutdown(); } //private bool _closingFromSystemMenu = false; private void Window_Closing(object sender, CancelEventArgs e) { /*if (!_closingFromSystemMenu && !CoreUtils.GetVersion().Equals("???")) { WindowState = WindowState.Minimized; e.Cancel = true; return; }*/ PanelHost.UnloadPanel(e); if (e.Cancel) { return; } App.IsClosing = true; FinalizeAutoTimesheet(); if (!CoreUtils.GetVersion().Equals("???")) scheduler.Stop(); CurrentTab = null; UpdateRibbonColors(); if (!CoreUtils.GetVersion().Equals("???")) if (station.ID != Guid.Empty) ExecuteLogout(); ShutDownTransport(); } #region Notifications + Heartbeat private void ReceiveNotification(Notification notification) { if (Security.CanView()) { Notifications.AddNotification(notification); } foreach(var panel in Panels.OfType()) { panel.AddNotification(notification); } } private void Notifications_Tick(object? sender, EventArgs e) { if (ClientFactory.UserGuid != Guid.Empty) { try { ReloadNotifications(); } catch (Exception err) { Logger.Send(LogType.Error, ClientFactory.UserID, string.Format("Exception in Notifications_Tick:ReloadNotifications() {0}\n{1}", err.Message, err.StackTrace)); } Heartbeat(); try { CheckIsLoggedOn(); } catch (Exception err2) { Logger.Send(LogType.Error, ClientFactory.UserID, string.Format("Exception in Notifications_Tick:CheckIsLoggedOn() {0}\n{1}", err2.Message, err2.StackTrace)); } } //else //{ // Logger.Send(LogType.Information, ClientFactory.UserID, "Notifications_Tick: ClientFactory.UserGuid is empty"); //} } private void CheckIsLoggedOn() { if (CoreUtils.GetVersion().Equals("???")) return; var bLogout = false; if (!Security.IsAllowed()) if (!IsClockedOn()) { Logger.Send(LogType.Information, ClientFactory.UserID, "User is no longer clocked in!"); bLogout = true; Dispatcher.Invoke(() => { Logout(); }); } if (!bLogout) new Client().Query( new Filter(x => x.User.ID).IsEqualTo(ClientFactory.UserGuid), Columns.None().Add(x => x.StationID), null, CoreRange.All, (o, e) => { if (e is RemoteException remote) { if (remote.Status == StatusCode.Unauthenticated) { Logger.Send(LogType.Information, ClientFactory.UserID, "User has been logged out"); Dispatcher.Invoke(() => { Logout("You have been logged out due to inactivity"); }); } else { Logger.Send(LogType.Information, ClientFactory.UserID, $"Error in CheckIsLoggedOn(): {CoreUtils.FormatException(remote)}"); } } else if (e is not null) { Logger.Send(LogType.Information, ClientFactory.UserID, $"Error in CheckIsLoggedOn(): {CoreUtils.FormatException(e)}"); } else if (o is not null) { var row = o.Rows.FirstOrDefault(); if (row == null) { station.ID = Guid.Empty; new Client().Save(station, "", (o1, e1) => { }); } else if (!row.Get(c => c.StationID).Equals(station.StationID)) { Logger.Send(LogType.Information, ClientFactory.UserID, "User logged in somewhere else!"); bLogout = true; Dispatcher.Invoke(() => { Logout(); }); } } } ); } private void Heartbeat() { //Task.Run(() => //{ try { bool IsClockedOn = this.IsClockedOn(); if (IsClockedOn) { if ((_kanbantrackingassignment != null) && (_kanbantrackingassignment.Actual.Finish < DateTime.Now.TimeOfDay)) { _kanbantrackingassignment.Actual.Finish = DateTime.Now.TimeOfDay; new Client().Save(_kanbantrackingassignment, ""); } if (Security.IsAllowed()) { if (ActivityHistory == null) ActivityHistory = new LocalConfiguration().Load(); var appname = OpenWindowGetter.GetActiveWindowProcess(); var title = OpenWindowGetter.GetActiveWindowTitle(); ActivityHistory.Activities[DateTime.Now] = (!string.IsNullOrWhiteSpace(appname) ? appname.Trim() + " - " : "") + title; new LocalConfiguration().Save(ActivityHistory); } } PanelHost.Heartbeat(); foreach(var window in SecondaryWindows.Values) { window.Heartbeat(); } } catch (Exception err) { Logger.Send(LogType.Error, ClientFactory.UserID, string.Format("Exception in Heartbeat: {0}\n{1}", err.Message, err.StackTrace)); } //}); } private void Notifications_Changed(object sender) { if (Notifications.IsActive) { Notifications.Visibility = Visibility.Visible; DockingGrid.ColumnDefinitions[1].Width = new GridLength(4, GridUnitType.Pixel); DockingGrid.ColumnDefinitions[2].Width = Equals(0.0, DockingGrid.ColumnDefinitions[2].Width.Value) ? new GridLength(300, GridUnitType.Pixel) : DockingGrid.ColumnDefinitions[2].Width; } else { Notifications.Visibility = Visibility.Collapsed; DockingGrid.ColumnDefinitions[1].Width = new GridLength(0, GridUnitType.Pixel); DockingGrid.ColumnDefinitions[2].Width = new GridLength(0, GridUnitType.Pixel); } } private void ReloadNotifications() { if (Security.CanView()) Notifications.Refresh(); if (!NotificationsWatchDog.IsEnabled) { //Logger.Send(LogType.Information, ClientFactory.UserID, "Enabling Heartbeat"); NotificationsWatchDog.IsEnabled = true; } } #endregion private void RibbonWindow_PreviewMouseUp(object sender, MouseButtonEventArgs e) { PanelHost.IncrementTrackingModuleClick(); } private void RibbonWindow_PreviewKeyUp(object sender, KeyEventArgs e) { PanelHost.IncrementTrackingModuleKey(); } #region Recording //private IntPtr myHook(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) //{ // switch (msg) // { // case 0x100: // if (_subject != null) // { // _recorder.Stop(); // pausestarted = DateTime.Now; // _subject.OnNext(new WampEvent() { Arguments = new[] { "EDIT" } }); // } // break; // default: // messages.TryGetValue(msg, out int value); // messages[msg] = value + 1; // break; // } // return IntPtr.Zero; //} private void RecordScreenButton_Click(object sender, RoutedEventArgs e) { ToggleRecording(); } private bool ToggleRecording() { if (!Security.IsAllowed()) return true; if (_recorder is null) { var windowInteropHelper = new WindowInteropHelper(this); var screen = Screen.FromHandle(windowInteropHelper.Handle); _screenleft = screen.WorkingArea.Left; _screentop = screen.WorkingArea.Top; _screenwidth = screen.WorkingArea.Width; _screenheight = screen.WorkingArea.Height; //VIDEO_WIDTH = _screenwidth; //VIDEO_HEIGHT = _screenheight; //SetupJPEGEncoder(); messages.Clear(); _bitmaps.Clear(); ConnectToRecordNotes(); StartAudio(); _recorder = new DispatcherTimer(); _recorder.Tick += (timer, args) => { TakeScreenShot(); }; _recorder.Interval = TimeSpan.FromMilliseconds(1000 / FRAMES_PER_SECOND); _recorder.Start(); VideoRecordingStatus.Source = PRSDesktop.Resources.videorecording.AsBitmapImage(); RecordingNotesStatus.Source = PRSDesktop.Resources.speechbubble.AsGrayScale().AsBitmapImage(); RecordingNotesButton.Visibility = Visibility.Visible; AudioRecordingStatus.Source = PRSDesktop.Resources.audiorecording.AsGrayScale().AsBitmapImage(); AudioRecordingButton.Visibility = Visibility.Visible; } else { using (new WaitCursor()) { StopRecording(); _recorder.Stop(); _recorder = null; DisconnectRecorderNotes(); VideoRecordingStatus.Source = PRSDesktop.Resources.videorecording.AsGrayScale().AsBitmapImage(); RecordingNotesStatus.Source = PRSDesktop.Resources.speechbubble.AsGrayScale().AsBitmapImage(); RecordingNotesButton.Visibility = Visibility.Hidden; AudioRecordingStatus.Source = PRSDesktop.Resources.audiorecording.AsGrayScale().AsBitmapImage(); AudioRecordingButton.Visibility = Visibility.Hidden; var filename = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyVideos), string.Format("PRS Screen Recording {0:yyyy-MM-dd hh-mm-ss-ff}.avi", DateTime.Now)); ProcessScreenShots(filename); } } return true; } private void StopRecording() { try { _audio?.StopRecording(); AudioRecordingStatus.Source = PRSDesktop.Resources.audiorecording.AsGrayScale().AsBitmapImage(); } catch (Exception e) { Logger.Send(LogType.Error, "", string.Format("Unable to stop Audio Recording: {0}\n{1}", e.Message, e.StackTrace)); } } private void StartAudio() { _audioMuted = true; _audioStream = new MemoryStream(); _audio = new WaveIn { DeviceNumber = 0, WaveFormat = new WaveFormat(44100, 16, 2) }; _audio.DataAvailable += (o, e) => { var buf = _audioMuted ? new byte[e.BytesRecorded] : e.Buffer; _audioStream.Write(buf, 0, e.BytesRecorded); }; try { _audio.StartRecording(); AudioRecordingStatus.Source = PRSDesktop.Resources.audiorecording.AsBitmapImage(); } catch (Exception e) { Logger.Send(LogType.Error, "", string.Format("Unable to start Audio Recording: {0}\n{1}", e.Message, e.StackTrace)); AudioRecordingStatus.Source = PRSDesktop.Resources.audiorecording.AsGrayScale().AsBitmapImage(); } } private bool ToggleRecordingAudio() { if (!Security.IsAllowed()) return true; if (_recorder != null) { _audioMuted = !_audioMuted; AudioRecordingStatus.Source = _audioMuted ? PRSDesktop.Resources.audiorecording.AsGrayScale().AsBitmapImage() : PRSDesktop.Resources.audiorecording.AsBitmapImage(); } return true; } private void DisconnectRecorderNotes() { if (_client != null) { _client.WriteAsync("QUIT"); _client.StopAsync(); _client = null; } } private void ConnectToRecordNotes() { var filename = Debugger.IsAttached ? "c:\\development\\comal\\prsrecordingnotes\\bin\\debug\\prsrecordingnotes.exe" : Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "recordingnotes", "prsrecordingnotes.exe"); Logger.Send(LogType.Information, "", "Recording Notes: " + filename); _recordingnotes = Process.Start(filename); //PipeSecurity sec = new System.IO.Pipes.PipeSecurity(); //sec.SetAccessRule(new PipeAccessRule("Everyone", PipeAccessRights.ReadWrite, AccessControlType.Allow)); _client = new PipeServer("PRSScreenRecorder", formatter:new BinaryFormatter()); //, sec); _client.ClientConnected += (o, args) => args.Connection.WriteAsync("Connected to PRSScreenRecorder"); _client.MessageReceived += (o, args) => { if (string.Equals(args.Message, "RESUME")) Dispatcher.Invoke(() => { _audio?.StartRecording(); totalpauses += DateTime.Now - pausestarted; _recorder?.Start(); Activate(); Focus(); }); }; _client.StartAsync(); } private bool ShowRecordingNotes() { if (_recorder != null) { _audio?.StopRecording(); _recorder.Stop(); pausestarted = DateTime.Now; _client?.WriteAsync("EDIT"); //_subject.OnNext(new WampEvent() { Arguments = new[] { "EDIT" } }); } return true; } private void TakeScreenShot() { using (var bmp = new ScreenCapture().CaptureScreen(_screenleft, _screentop, _screenwidth, _screenheight)) { var cursor = PointToScreen(Mouse.GetPosition(this)); using (var g = Graphics.FromImage(bmp)) { var brush = new SolidBrush(System.Drawing.Color.FromArgb(128, 255, 255, 0)); g.FillEllipse(brush, new RectangleF((float)cursor.X - 25F, (float)cursor.Y - 25F, 50F, 50F)); var pen = new Pen(System.Drawing.Color.Black, 0.75F); g.DrawLine(pen, (float)cursor.X - 10F, (float)cursor.Y, (float)cursor.X + 10F, (float)cursor.Y); g.DrawLine(pen, (float)cursor.X, (float)cursor.Y - 10F, (float)cursor.X, (float)cursor.Y + 10F); } var key = DateTime.Now - totalpauses; using (var reduced = ReduceBitmap(bmp, VIDEO_WIDTH, VIDEO_HEIGHT)) { var ms = new MemoryStream(); reduced.Save(ms, ImageFormat.Png); ms.Position = 0; _bitmaps[key] = ms; } } } public Bitmap ReduceBitmap(Bitmap original, int reducedWidth, int reducedHeight) { var reduced = new Bitmap(reducedWidth, reducedHeight); using (var dc = Graphics.FromImage(reduced)) { // Figure out the ratio var ratioX = (double)reducedWidth / original.Width; var ratioY = (double)reducedHeight / original.Height; // use whichever multiplier is smaller var ratio = ratioX < ratioY ? ratioX : ratioY; // now we can get the new height and width var newHeight = Convert.ToInt32(original.Height * ratio); var newWidth = Convert.ToInt32(original.Width * ratio); // Now calculate the X,Y position of the upper-left corner // (one of these will always be zero) var posX = Convert.ToInt32((reducedWidth - original.Width * ratio) / 2); var posY = Convert.ToInt32((reducedHeight - original.Height * ratio) / 2); dc.InterpolationMode = InterpolationMode.HighQualityBicubic; dc.Clear(System.Drawing.Color.Black); // white padding //dc.DrawImage(original, posX, posY, newWidth, newHeight); dc.DrawImage(original, new Rectangle(posX, posY, newWidth, newHeight), new Rectangle(0, 0, original.Width, original.Height), GraphicsUnit.Pixel); } return reduced; } public static byte[] BitmapToByteArray(Bitmap bitmap, int width, int height) { // and buffer of appropriate size for storing its bits var buffer = new byte[width * height * 4]; var pixelFormat = PixelFormat.Format32bppRgb; // Now copy bits from bitmap to buffer var bits = bitmap.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadOnly, pixelFormat); //Marshal.Copy(bits.Scan0, buffer, 0, buffer.Length); Marshal.Copy(bits.Scan0, buffer, 0, buffer.Length); bitmap.UnlockBits(bits); return buffer; } private void ProcessScreenShots(string filename) { if (!_bitmaps.Any() || _audioStream is null) return; Progress.Show("Saving Video"); var framRate = 10; using (var writer = new AviWriter(filename)) { writer.FramesPerSecond = framRate; writer.EmitIndex1 = true; _audioStream.Position = 0; var audio = writer.AddAudioStream(2); //audio.WriteBlock(_audioStream.GetBuffer(), 0, (int)_audioStream.Length); //File.WriteAllBytes(Path.ChangeExtension(filename, "wav"), _audioStream.GetBuffer()); var audioByteRate = audio.BitsPerSample / 8 * audio.ChannelCount * audio.SamplesPerSecond; var audioBlockSize = (int)(audioByteRate / writer.FramesPerSecond); var audioBuffer = new byte[audioBlockSize]; _audioStream.Position = 0; var encoder = new MJpegWpfVideoEncoder(VIDEO_WIDTH, VIDEO_HEIGHT, 50); var stream = writer.AddEncodingVideoStream(encoder, true, VIDEO_WIDTH, VIDEO_HEIGHT); //var encoder = new SharpAvi.Codecs.UncompressedVideoEncoder(VIDEO_WIDTH, VIDEO_HEIGHT); //var stream = writer.AddEncodingVideoStream(encoder,true,VIDEO_WIDTH, VIDEO_HEIGHT); //stream.Codec = CodecIds.Uncompressed; //stream.BitsPerPixel = BitsPerPixel.Bpp16; IEnumerable keys = _bitmaps.Keys.OrderBy(x => x); var start = keys.First(); var end = keys.Last(); for (var cur = start; cur <= end; cur = cur + TimeSpan.FromMilliseconds(100)) { Progress.SetMessage(string.Format("Processing ({0:F2} complete)", (cur - start).TotalSeconds * 100.0F / (end - start).TotalSeconds)); var key = keys.LastOrDefault(x => x <= cur); if (_bitmaps.ContainsKey(key)) { var frame = (Image.FromStream(_bitmaps[key]) as Bitmap)!; //frame.RotateFlip(RotateFlipType.RotateNoneFlipY); var buf = BitmapToByteArray(frame, stream.Width, stream.Height); stream.WriteFrame(true, buf, 0, buf.Length); if (_audioStream.Position < _audioStream.Length) { var bytes = _audioStream.Read(audioBuffer, 0, audioBlockSize); audio.WriteBlock(audioBuffer, 0, bytes); } } } writer.Close(); //foreach (var key in _bitmaps.Keys) // File.Delete(_bitmaps[key]); } Progress.SetMessage("Transcoding"); //var inputFile = new MediaFile { Filename = filename }; //var outputFile = new MediaFile { Filename = Path.ChangeExtension(filename, "mp4") }; //using (var engine = new Engine()) //{ // //engine.ConvertProgressEvent += ConvertProgressEvent; // engine.Convert(inputFile, outputFile); //} Progress.Close(); var dirName = Path.GetDirectoryName(filename); if (dirName is not null) { var startInfo = new ProcessStartInfo(dirName); startInfo.Verb = "open"; startInfo.UseShellExecute = true; Process.Start(startInfo); //MessageBox.Show("Recording Saved"); } } private void VideoRecordingButton_Click(object sender, RoutedEventArgs e) { ToggleRecording(); } private void AudioRecordingButton_Click(object sender, RoutedEventArgs e) { ToggleRecordingAudio(); } private void RecordingNotesButton_Click(object sender, RoutedEventArgs e) { ShowRecordingNotes(); } #endregion public static void ActivateWindow(Window window) { var hwnd = new WindowInteropHelper(window).EnsureHandle(); var threadId1 = GetWindowThreadProcessId(GetForegroundWindow(), IntPtr.Zero); var threadId2 = GetWindowThreadProcessId(hwnd, IntPtr.Zero); if (threadId1 != threadId2) { AttachThreadInput(threadId1, threadId2, true); SetForegroundWindow(hwnd); AttachThreadInput(threadId1, threadId2, false); } else { SetForegroundWindow(hwnd); } } private static IntPtr GetForegroundWindow() { var active = GetActiveWindow(); var window = Application.Current.Windows.OfType().SingleOrDefault(x => new WindowInteropHelper(x).Handle == active); var hwnd = new WindowInteropHelper(window).EnsureHandle(); return hwnd; } [DllImport("user32.dll", SetLastError = true)] private static extern IntPtr SetForegroundWindow(IntPtr hWnd); [DllImport("user32.dll")] private static extern uint GetWindowThreadProcessId(IntPtr hWnd, IntPtr ProcessId); [DllImport("user32.dll")] private static extern bool AttachThreadInput(uint idAttach, uint idAttachTo, bool fAttach); [DllImport("user32.dll")] private static extern IntPtr GetActiveWindow(); #region Check For Updates private string GetUpdateLocation() { if (App.DatabaseSettings.DatabaseType == DatabaseType.Networked) { 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 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 public void ClearActions() { foreach (var module in CurrentModules) { if (module.Parent is Fluent.RibbonGroupBox bar) bar.Items.Remove(module); } CurrentModules.Clear(); } public void ClearReports() { if (CurrentTab is not null) { var ReportsBar = FindRibbonBar(CurrentTab, x => x.Header.Equals("Print")); if (ReportsBar is not null) { ReportsBar.Visibility = Security.IsAllowed() ? Visibility.Visible : Visibility.Collapsed; ReportsBar.Items.Clear(); } } } public void CreateReport(PanelAction action) { if (CurrentTab is null) return; var ReportsBar = FindRibbonBar(CurrentTab, x => x.Header.Equals("Print")); if (ReportsBar is not null) { var button = new Fluent.Button { Header = action.Caption, LargeIcon = action.Image.AsBitmapImage(), MinWidth = 60 }; if (action.Menu is not null) { button.ContextMenu = action.Menu; } button.Click += (o, e) => { action.Execute(); }; ReportsBar.Items.Add(button); } } private NullConverter? _vis = null; public void CreatePanelAction(PanelAction action) { if (CurrentTab is null) return; if (_vis == null) { _vis = new(); _vis.Converting += (o, e) => { }; } var Actions = FindRibbonBar(CurrentTab, x => x.Header.Equals("Actions")); if (Actions is not null) { if (!CurrentModules.Any(x => x.GetType().Equals(typeof(RibbonSeparator)))) { var sep = new RibbonSeparator(); CurrentModules.Add(sep); Actions.Items.Add(sep); } if (action.OnPopulate != null) { Fluent.DropDownButton button = null; if (action.OnExecute != null) { button = new Fluent.SplitButton(); ((Fluent.SplitButton)button).Click += (o, e) => action.Execute(); } else button = new Fluent.DropDownButton(); button.MinWidth = 60; button.DataContext = action; button.DropDownOpened += (sender, args) => { button.ItemsSource = CreateMenuItems(action); }; button.Bind(Fluent.DropDownButton.HeaderProperty, x => x.Caption, null); button.Bind(Fluent.DropDownButton.LargeIconProperty, x => x.Image, new BitmapToBitmapImageConverter()); button.Bind(ContextMenuProperty, x => x.Menu); button.Bind(IsEnabledProperty, x => x.IsEnabled); button.Bind(Fluent.DropDownButton.VisibilityProperty, x => x.Visibility, _vis); Actions.Items.Add(button); CurrentModules.Add(button); } else { var button = new Fluent.Button() { MinWidth = 60, DataContext = action }; button.Bind(Fluent.Button.HeaderProperty, x => x.Caption, null); button.Bind(Fluent.Button.LargeIconProperty, x => x.Image, new BitmapToBitmapImageConverter()); button.Bind(Fluent.Button.ContextMenuProperty, x => x.Menu); button.Bind(Fluent.Button.IsEnabledProperty, x => x.IsEnabled); button.Bind(Fluent.Button.VisibilityProperty, x => x.Visibility, _vis); button.Click += (o, e) => action.Execute(); Actions.Items.Add(button); CurrentModules.Add(button); } } } private static ObservableCollection CreateMenuItems(PanelAction action) { var items = new ObservableCollection(); var entries = action.Populate(); if (entries != null) { foreach (var entry in entries) { var item = new Fluent.MenuItem() { Header = entry.Caption, DataContext = entry.Data, Icon = entry.Image?.AsBitmapImage() }; item.Click += (o, eventArgs) => entry.Execute(); items.Add(item); } } return items; } #endregion #region Tracking Kanban private void SelectTask_Click(object sender, RoutedEventArgs e) { ContextMenu menu = new ContextMenu(); MenuItem others = new MenuItem() { Header = "Other Tasks" }; using (new WaitCursor()) { var kanbans = new Client().Query( new Filter(x => x.Employee.UserLink.ID).IsEqualTo(ClientFactory.UserGuid) .And(x => x.Kanban.Completed).IsEqualTo(DateTime.MinValue) .And(x => x.Kanban.Closed).IsEqualTo(DateTime.MinValue), Columns.None().Add(x => x.Kanban.ID) .Add(x => x.Kanban.Number) .Add(x => x.Kanban.Title) .Add(x => x.Assignee), new SortOrder(x => x.Kanban.Number, SortDirection.Ascending) ); foreach (var row in kanbans.Rows) { CreateTaskMenu(row.Get(c => c.Assignee) ? menu.Items : others.Items, String.Format("{0} {1}", row.Get(c => c.Kanban.Number), row.Get(c => c.Kanban.Title)), row.Get(c => c.Kanban.ID) ); } menu.Items.Add(new Separator()); menu.Items.Add(others); menu.Items.Add(new Separator()); CreateTaskMenu(menu.Items, "(No Task Selected)", Guid.Empty); } menu.IsOpen = true; } private Assignment? _kanbantrackingassignment = null; private void SetTrackingKanban(Guid kanbanID, string header) { SelectedTaskName.Content = header; var createNewAssignment = false; if (_kanbantrackingassignment is not null) { if (_kanbantrackingassignment.Actual.Finish < DateTime.Now.TimeOfDay) { _kanbantrackingassignment.Actual.Finish = DateTime.Now.TimeOfDay; new Client().Save(_kanbantrackingassignment, ""); } // Update Existing Kanban if (kanbanID == Guid.Empty) _kanbantrackingassignment = null; else if (_kanbantrackingassignment.Task.ID != kanbanID) { createNewAssignment = true; } } else if (kanbanID != Guid.Empty) { createNewAssignment = true; } if (createNewAssignment) { _kanbantrackingassignment = new Assignment(); _kanbantrackingassignment.Task.ID = kanbanID; _kanbantrackingassignment.EmployeeLink.ID = App.EmployeeID; _kanbantrackingassignment.Title = header; _kanbantrackingassignment.Date = DateTime.Today; _kanbantrackingassignment.Actual.Start = DateTime.Now.TimeOfDay; _kanbantrackingassignment.Actual.Finish = DateTime.Now.TimeOfDay.Add(new TimeSpan(0, 2, 0)); new Client().Save(_kanbantrackingassignment, ""); } } private void ClearTrackingKanban() => SetTrackingKanban(Guid.Empty, "(No Task Selected)"); private void CreateTaskMenu(ItemCollection items, String title, Guid id) { var item = new MenuItem() { Header = title, Tag = id }; item.Click += (o, args) => { if (o is not MenuItem menu) return; SetTrackingKanban((Guid)item.Tag, (menu.Header as string) ?? ""); }; items.Add(item); } #endregion private void DockPanelBorder_IsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e) { if (sender is not Border border || !border.IsVisible) return; var dock = border.Child is IDockPanel panel ? panel : border.Child?.FindVisualChildren().FirstOrDefault(); if (dock is null) return; dock.Refresh(); } private void DockPanel_OnIsActiveChanged(object? sender, EventArgs e) { if (sender is not LayoutAnchorable layout) return; var content = layout.Content as DependencyObject; var dock = content is IDockPanel panel ? panel : content?.FindVisualChildren().FirstOrDefault(); if (dock is null) return; if (layout.IsActive && layout.IsVisible) dock.Refresh(); } private void _ribbon_OnPreviewMouseDoubleClick(object sender, MouseButtonEventArgs e) { e.Handled = true; } #region Backstage Functions private enum DatabaseConfigurationResult { RestartRequired, RestartNotRequired, Cancel } private DatabaseConfigurationResult ShowDatabaseConfiguration() { DatabaseConfigurationResult result; var config = new DataBaseConfiguration(); if (config.ShowDialog() == true) { var newsettings = new LocalConfiguration(App.Profile).Load(); if (newsettings.RestartRequired(App.DatabaseSettings)) { result = DatabaseConfigurationResult.RestartRequired; } else { result = DatabaseConfigurationResult.RestartNotRequired; } if ((newsettings.DatabaseType == DatabaseType.Standalone) && (newsettings.ColorScheme != App.DatabaseSettings.ColorScheme)) { App.DatabaseSettings.ColorScheme = newsettings.ColorScheme; ApplyColorScheme(); } } else { result = DatabaseConfigurationResult.Cancel; } return result; } private void DatabaseSettings_OnClick(object sender, RoutedEventArgs e) { switch (ShowDatabaseConfiguration()) { case DatabaseConfigurationResult.RestartRequired: MessageBox.Show("Please restart the application to apply these changes!"); break; } } private void CompanyInformation_OnClick(object sender, RoutedEventArgs e) { var info = new Client().Load().FirstOrDefault(); if (info == null) info = new CompanyInformation(); new DynamicDataGrid().EditItems(new[] { info }); } private void SecurityDefaultsButton_OnClick(object sender, RoutedEventArgs e) { var window = new GlobalTokenWindow(); window.ShowDialog(); Security.Reset(); } private void SystemLogsButton_OnClick(object sender, RoutedEventArgs e) { var logfile = Path.Combine(CoreUtils.GetPath(), string.Format("{0:yyyy-MM-dd}.log", DateTime.Today)); if (File.Exists(logfile)) { var startInfo = new ProcessStartInfo("notepad.exe", logfile); startInfo.Verb = "open"; startInfo.UseShellExecute = true; Process.Start(startInfo); } else { MessageBox.Show(logfile + " does not exist!"); } } private void DocumentTypeList_OnClick(object sender, RoutedEventArgs e) { var list = new MasterList(typeof(DocumentType)); list.ShowDialog(); } private void EditDetailsButton_OnClick(object sender, RoutedEventArgs e) { var employee = new Client().Query( new Filter(x => x.UserLink.ID).IsEqualTo(ClientFactory.UserGuid)) .Rows.FirstOrDefault()?.ToObject(); var item = new MyDetailsConfiguration(employee); item.User = ClientFactory.UserGuid; var buttons = new DynamicEditorButtons(); buttons.Add("Change Password", null, item, (sender, item) => { var details = (item as MyDetailsConfiguration)!; var changePassword = new ChangePassword(details.User); if (changePassword.ShowDialog() == true && changePassword.Password != null) { var newUser = new User(); newUser.ID = details.User; ChangePassword.ChangeUserPassword(newUser, changePassword.Password); new Client().Save(newUser, "Changed Password"); MessageBox.Show("Password changed!"); } }); var editor = new DynamicEditorForm(typeof(MyDetailsConfiguration), buttons: buttons); if (employee == null) { editor.OnFormCustomiseEditor += (sender, items, column, editor) => { editor.Editable = Editable.Disabled; }; } editor.Items = new BaseObject[] { item }; if (editor.ShowDialog() == true) { if (employee != null) { item.SaveTo(employee); new Client().Save(employee, "Edited by user"); } } } private void LogoutButton_OnClick(object sender, RoutedEventArgs e) { Logout(); } private void LoginButton_OnClick(object sender, RoutedEventArgs e) { if (DoLogin() == ValidationStatus.VALID) AfterLogin(null); } private void ExitButton_OnClick(object sender, RoutedEventArgs e) { //_closingFromSystemMenu = true; Close(); } #endregion }