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.Rpc; using InABox.Scripting; using InABox.Wpf; 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.Controls; 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 SortDirection = InABox.Core.SortDirection; using InABox.Wpf.Reports; using Comal.Classes.SecurityDescriptors; using System.Threading; using H.Formatters; using PRSDesktop.Forms.Issues; using Brushes = System.Windows.Media.Brushes; using System.Windows.Media.Imaging; namespace PRSDesktop; public enum PanelType { InPlace, NewWindow } public class SimpleCommand : ICommand { public Action OnExecute { get; private set; } public bool CanExecute(object? parameter) => true; public event EventHandler? CanExecuteChanged { add => CommandManager.RequerySuggested += value; remove => CommandManager.RequerySuggested -= value; } public void Execute(object? parameter) { OnExecute?.Invoke(); } public SimpleCommand(Action onExecute) { OnExecute = onExecute; } } /// /// 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"); if (SupportUtils.CheckForUpdates()) { Logger.Send(LogType.Information, "", "Update found, closing application."); Close(); return; } 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(); StoreUtils.RegisterClasses(); PRSSharedUtils.RegisterClasses(); WPFUtils.RegisterClasses(); ReportUtils.RegisterClasses(); ConfigurationUtils.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"); 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. Progress.ShowModal("Loading PRS", progress => { 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); } else { ConfigureMainScreen(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; DynamicGridUtils.FilterForeground = ThemeManager.FilterForegroundBrush; //_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 SetFrameworkItemVisibility(SendNotification, Security.CanView()); SetFrameworkItemVisibility(Notifications, Security.CanView()); SetFrameworkItemVisibility(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() { SetFrameworkItemVisibility(CompanyInformation, Security.CanView()); SetVisibleIfAny(BackstageSeparator0, CompanyInformation); SetFrameworkItemVisibility(SecurityDefaultsButton, ClientFactory.IsSupported() && Security.IsAllowed()); SetVisibleIfAny(BackstageSeparator1, SecurityDefaultsButton); BackstageSeparator1a.Visibility = Visibility.Visible; SystemLogsButton.Visibility = Visibility.Visible; SetFrameworkItemVisibility(DocumentTypeList, ClientFactory.IsSupported() && Security.IsAllowed()); SetFrameworkItemVisibility(EventList, Security.IsAllowed()); SetVisibleIfAny(BackstageSeparator2, DocumentTypeList, EventList); //SetModuleVisibility<>(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(ProblemsDock, Problems); SetupDock(RequisitionsDock, Requisitions); _ribbon.InvalidateArrange(); } private void SetupDashboardsTab(bool bMaps) { if (!Security.IsAllowed()) return; SetModuleVisibility(DashboardsDashboardButton, Security.IsAllowed()); SetModuleVisibility(DashboardMessagesButton, Security.CanView()); SetModuleVisibility(DashboardsTaskButton, ClientFactory.IsSupported() && Security.IsAllowed()); SetModuleVisibility(DashboardsAttendanceButton, ClientFactory.IsSupported() && Security.IsAllowed()); SetModuleVisibility(DashboardsMapButton, bMaps); SetModuleVisibility(DashboardsDailyReportButton, ClientFactory.IsSupported() && Security.IsAllowed()); SetModuleVisibility(FactoryProductivityButton, ClientFactory.IsSupported() && Security.IsAllowed() && Security.IsAllowed()); SetModuleVisibility(TemplateAnalysisButton, ClientFactory.IsSupported() && Security.IsAllowed() && Security.IsAllowed()); SetModuleVisibility(FactoryAnalysisButton, ClientFactory.IsSupported() && Security.IsAllowed() && Security.IsAllowed()); SetModuleVisibility(DatabaseActivityButton, ClientFactory.IsSupported() && Security.IsAllowed() && Security.IsAllowed()); SetModuleVisibility(UserActivityButton, ClientFactory.IsSupported() && Security.IsAllowed() && Security.IsAllowed()); SetModuleVisibility(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; SetModuleVisibility(DigitalFormsDashboardButton, Security.IsAllowed()); SetModuleVisibility(DigitalFormsMessagesButton, Security.CanView()); SetModuleVisibility(DigitalFormsTaskButton, ClientFactory.IsSupported() && Security.IsAllowed()); SetModuleVisibility(DigitalFormsAttendanceButton, ClientFactory.IsSupported() && Security.IsAllowed()); SetModuleVisibility(DigitalFormsMapButton, bMaps); SetModuleVisibility(DigitalFormsDailyReportButton, ClientFactory.IsSupported() && Security.IsAllowed()); SetModuleVisibility(DigitalFormsFormsLibraryButton, ClientFactory.IsSupported() && Security.CanView() && Security.IsAllowed()); SetModuleVisibility(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; SetModuleVisibility(EquipmentDashboardButton, Security.IsAllowed()); SetModuleVisibility(EquipmentMessagesButton, Security.CanView()); SetModuleVisibility(EquipmentTaskButton, ClientFactory.IsSupported() && Security.IsAllowed()); SetModuleVisibility(EquipmentAttendanceButton, ClientFactory.IsSupported() && Security.IsAllowed()); SetModuleVisibility(EquipmentMapButton, bMaps); SetModuleVisibility(EquipmentDailyReportButton, ClientFactory.IsSupported() && Security.IsAllowed()); SetModuleVisibility(EquipmentButton, ClientFactory.IsSupported() && Security.CanView() && Security.IsAllowed()); SetModuleVisibility(EquipmentMaintenanceButton, ClientFactory.IsSupported() && Security.CanView() && Security.IsAllowed() ); SetModuleVisibility(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); SetModuleVisibility(TrackersMasterList, Security.CanView() && Security.IsAllowed()); SetTabVisibleIfAny(EquipmentTab, EquipmentButton, TrackersMasterList); } private void SetupAccountsTab(bool bMaps) { if (!Security.IsAllowed()) return; SetModuleVisibility(AccountsDashboardButton, Security.IsAllowed()); SetModuleVisibility(AccountsMessagesButton, Security.CanView()); SetModuleVisibility(AccountsTaskButton, ClientFactory.IsSupported() && Security.IsAllowed()); SetModuleVisibility(AccountsAttendanceButton, ClientFactory.IsSupported() && Security.IsAllowed()); SetModuleVisibility(AccountsMapButton, bMaps); SetModuleVisibility(AccountsDailyReportButton, ClientFactory.IsSupported() && Security.IsAllowed()); SetModuleVisibility(CustomerList, ClientFactory.IsSupported() && Security.CanView() && Security.IsAllowed()); SetModuleVisibility(InvoiceList, ClientFactory.IsSupported() && Security.CanView() && Security.IsAllowed()); SetModuleVisibility(ReceiptList, ClientFactory.IsSupported() && Security.CanView() && Security.IsAllowed()); SetModuleVisibility(SupplierList, ClientFactory.IsSupported() && Security.CanView() && Security.IsAllowed()); SetModuleVisibility(AccountsDataButton, Security.IsAllowed()); SetModuleVisibility(PurchasesList, ClientFactory.IsSupported() && Security.CanView() && Security.IsAllowed()); SetModuleVisibility(BillsList, ClientFactory.IsSupported() && Security.CanView() && Security.IsAllowed()); SetModuleVisibility(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; SetModuleVisibility(HumanResourcesDashboardButton, Security.IsAllowed()); SetModuleVisibility(HumanResourcesMessagesButton, Security.CanView()); SetModuleVisibility(HumanResourcesTaskButton, ClientFactory.IsSupported() && Security.IsAllowed()); SetModuleVisibility(HumanResourcesAttendanceButton, ClientFactory.IsSupported() && Security.IsAllowed()); SetModuleVisibility(HumanResourcesMapButton, bMaps); SetModuleVisibility(HumanResourcesDailyReportButton, ClientFactory.IsSupported() && Security.IsAllowed()); SetModuleVisibility(CalendarButton, Security.CanView() && Security.IsAllowed()); SetModuleVisibility(EmployeePlannerButton, Security.CanView() && Security.IsAllowed()); SetModuleVisibility(TimesheetsButton, Security.CanView() && Security.IsAllowed()); SetModuleVisibility(LeaveRequestsButton, Security.CanView() && Security.IsAllowed()); SetModuleVisibility(OrgChartButton, ClientFactory.IsSupported() && Security.IsAllowed() ); SetModuleVisibility(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); SetModuleVisibility(UsersButton, Security.CanView() && Security.IsAllowed()); SetModuleVisibility(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; SetModuleVisibility(ProductsDashboardButton, Security.IsAllowed()); SetModuleVisibility(ProductsMessagesButton, Security.CanView()); SetModuleVisibility(ProductsTaskButton, Security.CanView()); SetModuleVisibility(ProductsAttendanceButton, Security.IsAllowed()); SetModuleVisibility(ProductsMapButton, bMaps); SetModuleVisibility(ProductsDailyReportButton, ClientFactory.IsSupported() && Security.IsAllowed()); SetModuleVisibility(ProductsMasterList, Security.CanView() && Security.IsAllowed()); SetModuleVisibility(StockLocationList, Security.CanView() && Security.IsAllowed()); SetModuleVisibility(StockMovementList, Security.CanView() && Security.IsAllowed()); SetModuleVisibility(StockForecastButton, Security.CanView() && Security.CanView() && Security.IsAllowed()); SetModuleVisibility(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; SetModuleVisibility(LogisticsDashboardButton, Security.IsAllowed()); SetModuleVisibility(LogisticsMessagesButton, Security.CanView()); SetModuleVisibility(LogisticsTaskButton, ClientFactory.IsSupported() && Security.IsAllowed()); SetModuleVisibility(LogisticsAttendanceButton, ClientFactory.IsSupported() && Security.IsAllowed()); SetModuleVisibility(LogisticsMapButton, bMaps); SetModuleVisibility(LogisticsDailyReportButton, ClientFactory.IsSupported() && Security.IsAllowed()); SetModuleVisibility(ReadyToGoItemsButton, ClientFactory.IsSupported() && Security.IsAllowed() && Security.IsAllowed()); SetModuleVisibility(DispatchButton, Security.CanView() && Security.CanView() && Security.IsAllowed()); SetModuleVisibility(RequisitionsButton, Security.CanView() && Security.IsAllowed()); SetModuleVisibility(DeliveriesButton, Security.IsAllowed() && Security.IsAllowed()); SetModuleVisibility(DeliveredItemsButton, ClientFactory.IsSupported() && Security.IsAllowed() && Security.IsAllowed()); SetModuleVisibility(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; SetModuleVisibility(ManufacturingDashboardButton, Security.IsAllowed()); SetModuleVisibility(ManufacturingMessagesButton, Security.CanView()); SetModuleVisibility(ManufacturingTaskButton, ClientFactory.IsSupported() && Security.IsAllowed()); SetModuleVisibility(ManufacturingAttendanceButton, ClientFactory.IsSupported() && Security.IsAllowed()); SetModuleVisibility(ManufacturingMapButton, bMaps); SetModuleVisibility(ManufacturingDailyReportButton, ClientFactory.IsSupported() && Security.IsAllowed()); SetVisibleIfAny(ManufacturingTaskSeparator, new FrameworkElement[] { ManufacturingDashboardButton, ManufacturingMessagesButton, ManufacturingTaskButton, ManufacturingAttendanceButton, ManufacturingMapButton, ManufacturingDailyReportButton }); SetModuleVisibility(DesignManagementButton, Security.CanView() && Security.IsAllowed()); SetFrameworkItemVisibility(ManufacturingDesignSeparator, Security.CanView() && Security.IsAllowed()); SetModuleVisibility(FactoryStatusButton, ClientFactory.IsSupported() && Security.IsAllowed() && Security.IsAllowed()); SetModuleVisibility(FactoryAllocationButton, ClientFactory.IsSupported() && Security.IsAllowed() && Security.IsAllowed()); //SetModuleVisibility<>(FactoryScheduleButton, ClientFactory.IsSupported() && ClientFactory.IsEnabled<>()); SetModuleVisibility(FactoryFloorButton, ClientFactory.IsSupported() && Security.IsAllowed() && Security.IsAllowed()); //SetModuleVisibility<>(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; SetModuleVisibility(ProjectsDashboardButton, Security.IsAllowed()); SetModuleVisibility(ProjectMessagesButton, Security.CanView()); SetModuleVisibility(ProjectTaskButton, ClientFactory.IsSupported() && Security.IsAllowed()); SetModuleVisibility(ProjectAttendanceButton, ClientFactory.IsSupported() && Security.IsAllowed()); SetModuleVisibility(ProjectsMapButton, bMaps); SetModuleVisibility(ProjectDailyReportButton, ClientFactory.IsSupported() && Security.IsAllowed()); SetModuleVisibility(ProjectsButton, Security.CanView() && Security.IsAllowed()); //SetModuleVisibility<>(ServiceButton, Security.CanView() && Security.IsAllowed()); SetModuleVisibility(ProjectPlannerButton, Security.CanView() && Security.IsAllowed()); SetVisibleIfEither(ProjectTaskSeparator, new FrameworkElement[] { ProjectsDashboardButton, ProjectMessagesButton, ProjectTaskButton, ProjectAttendanceButton, ProjectsMapButton, ProjectDailyReportButton }, new FrameworkElement[] { QuotesButton, ProjectsButton, ProjectPlannerButton }); SetModuleVisibility(QuotesButton, Security.CanView() && Security.IsAllowed()); SetModuleVisibility(KitsMasterList, Security.CanView() && Security.IsAllowed()); SetModuleVisibility(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(); var module = app.Settings.ContainsKey("CurrentPanel") ? !string.IsNullOrWhiteSpace(app.Settings["CurrentPanel"]) ? app.Settings["CurrentPanel"].Split([" / "], StringSplitOptions.None) : [ "Human Resources", "Task List"] : [ "Human Resources", "Task List"]; try { var bFound = false; 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; if (button.Command is SimpleCommand command) command.Execute(null); //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; } } } #region LoadWindow / LoadSecondaryWindow private T? LoadWindow(Fluent.Button sender) where T : class, IBasePanel, new() { return LoadWindow(sender, new CancelEventArgs()); } public IBasePanel? LoadWindow(Type t, Fluent.Button sender, CancelEventArgs cancel) { 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(t, 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 T? LoadWindow(Fluent.Button sender, CancelEventArgs cancel) where T : class, IBasePanel, new() { return LoadWindow(typeof(T), sender, cancel) as T; } private void LoadSecondaryWindow(Type t, Fluent.Button sender) { var id = Guid.NewGuid(); var window = new Tuple( t.EntityName(), sender.Header?.ToString() ?? "", 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 LoadSecondaryWindow(Fluent.Button sender) where T : class, IBasePanel, new() => LoadSecondaryWindow(typeof(T), sender); // 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(); // } #endregion 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.DefaultStore = typeof(BaseStore<>); 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, "", "Checking Support Ticket Status"); CheckSupportTicketStatus(); Logger.Send(LogType.Information, "", "Loading employee"); LoadCurrentEmployee(); if (CheckTimesheetBypass(true)) { UpdateCurrentLogin(); } else { Dispatcher.Invoke(() => { 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) { 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 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 SetFrameworkItemVisibility(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 Fluent.Button rb) { CustomModules.Register(rb.Header?.ToString() ?? ""); rb.IsEnabled = !OutstandingDailyReports(false); } } private void SetModuleVisibility(Fluent.Button button, bool visible) where T : class, IBasePanel, new() { SetFrameworkItemVisibility(button,visible); button.MinWidth = 60; if (button.Command == null) { button.Command = new SimpleCommand(() => LoadWindow(button)); var menu = new ContextMenu(); menu.Items.Add(new MenuItem() { Header = "Open in New Window", Icon = new System.Windows.Controls.Image() { Source = PRSDesktop.Resources.target.AsBitmapImage() }, Command = new SimpleCommand(() => LoadSecondaryWindow(button)) }); button.ContextMenu = menu; } } 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 Accounts #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 RefreshMenu_Click(object sender, RoutedEventArgs e) { PanelHost.Refresh(); } //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 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 }); } 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 Issues_Click(object sender, RoutedEventArgs e) { try { IssuesWindow.Execute(); CheckSupportTicketStatus(); } catch(Exception err) { MessageWindow.ShowError("Could not load issues.", err); } } private void StartForm(DigitalForm form) where TEntityForm : EntityForm, new() where TEntity : Entity, new() where TEntityLink : EntityLink, new() { var entityForm = new TEntityForm(); entityForm.Form.CopyFrom(form); entityForm.Description = form.Description; entityForm.FormStarted = DateTime.Now; 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) { ISubPanelHost.Global.ShutdownSubPanels(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 { CheckSupportTicketStatus(); 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 CheckSupportTicketStatus() { Dispatcher.BeginInvoke(() => { IssuesButton.Background = IssuesWindow.Check() ? new SolidColorBrush(Colors.Red) { Opacity = 0.5 } : Brushes.Transparent; }); } private void Notifications_Changed(object sender) { if (Notifications.IsActive) { 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(); } 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 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 = 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.Visibility = Security.IsAllowed() ? Visibility.Visible : Visibility.Collapsed; 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; 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 class TrackingKanbanFilterItemComponent : DynamicGridFilterComponent { public TrackingKanbanFilterItemComponent() : base( new GlobalConfiguration(nameof(Kanban)), new UserConfiguration(nameof(Kanban))) { ButtonText = "Filter"; } public string Text { get; private set; } = "Filter"; public Bitmap? Image { get; private set; } protected override void UpdateButtonText(Bitmap image, string text) { Text = text; Image = image; } } private TrackingKanbanFilterItemComponent? _trackingKanbanFilterComponent; private TrackingKanbanFilterItemComponent TrackingKanbanFilterComponent { get { if(_trackingKanbanFilterComponent is null) { _trackingKanbanFilterComponent = new(); _trackingKanbanFilterComponent.OnFiltersSelected += _trackingKanbanFilterComponent_OnFiltersSelected; } return _trackingKanbanFilterComponent; } } private ContextMenu? _trackingKanbanMenu; private MenuItem? _trackingKanbanFilterMenu; private void _trackingKanbanFilterComponent_OnFiltersSelected(DynamicGridSelectedFilterSettings filters) { if (_trackingKanbanMenu is null) return; if (!filters.MultipleFilters) { _trackingKanbanMenu.IsOpen = false; } if(_trackingKanbanFilterMenu is not null) { _trackingKanbanFilterMenu.Header = TrackingKanbanFilterComponent.Text; _trackingKanbanFilterMenu.Icon = new System.Windows.Controls.Image() { Source = TrackingKanbanFilterComponent.Image?.AsBitmapImage(24, 24) }; } } private void SelectTask_Click(object sender, RoutedEventArgs e) { var menu = new ContextMenu(); var others = new MenuItem() { Header = "Other Tasks" }; var waiting = new MenuItem() { Header = "Waiting Tasks" }; using (new WaitCursor()) { var filter = 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); var kanbanFilter = TrackingKanbanFilterComponent.GetFilter(); if(kanbanFilter is not null) { filter = filter.And(x => x.Kanban.ID).InQuery(kanbanFilter, x => x.ID); } var kanbans = Client.Query( filter, Columns.None() .Add(x => x.Kanban.ID) .Add(x => x.Kanban.Number) .Add(x => x.Kanban.Title) .Add(x => x.Kanban.Status) .Add(x => x.Assignee), new SortOrder(x => x.Kanban.Number, SortDirection.Ascending)); foreach (var subscriber in kanbans.ToObjects()) { CreateTaskMenu(subscriber.Assignee ? (subscriber.Kanban.Status != KanbanStatus.Waiting ? menu : waiting) : others, $"{subscriber.Kanban.Number} {subscriber.Kanban.Title}", subscriber.Kanban.ID); } menu.AddSeparatorIfNeeded(); if(others.Items.Count > 0) { menu.Items.Add(others); } if(waiting.Items.Count > 0) { menu.Items.Add(waiting); } menu.AddSeparatorIfNeeded(); CreateTaskMenu(menu, "(No Task Selected)", Guid.Empty); if(_kanbantrackingassignment is not null && _kanbantrackingassignment.Task.ID != Guid.Empty) { menu.AddItem("View Task", null, _kanbantrackingassignment.Task.ID, ViewTrackingKanban_Click); } menu.AddSeparatorIfNeeded(); var filterItem = menu.AddItem(TrackingKanbanFilterComponent.Text, TrackingKanbanFilterComponent.Image, null); TrackingKanbanFilterComponent.PopulateMenu(filterItem); _trackingKanbanFilterMenu = filterItem; } menu.IsOpen = true; _trackingKanbanMenu = menu; } private void ViewTrackingKanban_Click(Guid guid) { var item = Client.Query( new Filter(x => x.ID).IsEqualTo(guid), DynamicGridUtils.LoadEditorColumns()) .ToObjects().FirstOrDefault(); if (item is null) return; var grid = DynamicGridUtils.CreateDynamicGrid(typeof(DynamicGrid<>)); grid.EditItemsNonModal(ISubPanelHost.Global, [item], (items, saved) => { }); } private Assignment? _kanbantrackingassignment = null; private CoreFilterDefinitions? _kanbanTrackingFilter = 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; 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)); Client.Save(_kanbantrackingassignment, ""); } } private void ClearTrackingKanban() => SetTrackingKanban(Guid.Empty, "(No Task Selected)"); private void CreateTaskMenu(ItemsControl menu, string title, Guid id) { menu.AddCheckMenuItem(title, (title, id), TrackingKanbanMenuItem_Click, isChecked: _kanbantrackingassignment?.Task.ID == id); } private void TrackingKanbanMenuItem_Click((string title, Guid id) tuple, bool isChecked) { if (isChecked) { SetTrackingKanban(tuple.id, tuple.title); } } #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 = CoreUtils.GetLogFile(); 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 CheckForUpdates_OnClick(object sender, RoutedEventArgs e) { if (SupportUtils.CheckForUpdates()) { Close(); } else { if (MessageWindow.ShowYesNo( "You appear to be using the latest version already!\n\nRun the installer anyway?", "Update")) { if (SupportUtils.DownloadAndRunInstaller()) { Close(); } } } } private void OpenSupportSession_OnClick(object sender, RoutedEventArgs e) { SupportUtils.OpenSupportSession(); } private void DocumentTypeList_OnClick(object sender, RoutedEventArgs e) { var list = new MasterList(typeof(DocumentType)); list.ShowDialog(); } private void EventList_Click(object sender, RoutedEventArgs e) { var list = new MasterList(typeof(Event)); 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.SetID(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 }