using System; using System.Collections.Generic; 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.Net.Mail; using System.Reflection; using System.Runtime.InteropServices; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Controls.Primitives; 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.Client.IPC; using InABox.Clients; using InABox.Configuration; using InABox.Core; using InABox.Database; using InABox.Database.SQLite; using InABox.DeviceIdentifier; using InABox.DynamicGrid; using InABox.Mail; using InABox.Reports; using InABox.Core.Reports; using InABox.Scripting; using InABox.WPF; using NAudio.Wave; using PRS.Shared; using InABox.WPF.Themes; using NPOI.SS.Formula.Functions; using PRSDesktop.Configuration; using PRSDesktop.Forms; using PRSServer; using SharpAvi.Codecs; using SharpAvi.Output; using Syncfusion.UI.Xaml.Maps; 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 ValidationResult = InABox.Clients.ValidationResult; using System.Diagnostics.CodeAnalysis; using System.Runtime.InteropServices; using FastReport.Export.Email; using InABox.Wpf; using javax.xml.crypto; using PRSDesktop.Components.Spreadsheet; using InABox.Wpf.Reports; namespace PRSDesktop { /// /// Interaction logic for Main.xaml /// public partial class MainWindow : IPanelHost { //private const int WM_LBUTTONDOWN = 0x201; private static PipeServer? _client; private WaveIn? _audio; private bool _audioMuted; private MemoryStream? _audioStream; private readonly Dictionary _bitmaps = new(); private Console? _console; private Dictionary> _notes = new(); //Dictionary Panels = new Dictionary(); 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 IBasePanel? CurrentPanel; private int CurrentPanel_Clicks; private int CurrentPanel_Keys; private string CurrentPanel_Label = ""; private DateTime CurrentPanel_Ticks = DateTime.MinValue; 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; public MainWindow() { NotificationsWatchDog = new DispatcherTimer { IsEnabled = false }; NotificationsWatchDog.Tick += Notifications_Tick; NotificationsWatchDog.Interval = new TimeSpan(0, 2, 0); ClientFactory.Notifications.AddHandler(ReceiveNotification); ClientFactory.RegisterMailer(EmailType.IMAP, typeof(IMAPMailer)); ClientFactory.RegisterMailer(EmailType.Exchange, typeof(ExchangeMailer)); ClientFactory.OnLog += (_, message) => { Logger.Send(LogType.Information, ClientFactory.UserID, message); }; 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); DatabaseType = App.DatabaseSettings.DatabaseType; switch (DatabaseType) { case DatabaseType.Standalone: ClientFactory.SetClientType(typeof(LocalClient<>), "Wpf", CoreUtils.GetVersion()); DbFactory.ColorScheme = App.DatabaseSettings.ColorScheme; DbFactory.Logo = App.DatabaseSettings.Logo; break; case DatabaseType.Networked: var url = JsonClient.Ping(App.DatabaseSettings.URLs, out DatabaseInfo info); ClientFactory.SetClientType(typeof(JsonClient<>), "Wpf", CoreUtils.GetVersion(), url, true); break; case DatabaseType.Local: ClientFactory.SetClientType(typeof(PipeIPCClient<>), "Wpf", CoreUtils.GetVersion(), DatabaseServerProperties.GetPipeName(App.DatabaseSettings.LocalServerName)); break; } ThemeManager.BaseColor = Colors.CornflowerBlue; Progress.DisplayImage = PRSDesktop.Resources.splash_small.AsBitmapImage(); try { var dbInfo = new Client().Info(); 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 { } InitializeComponent(); 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()})"; CheckForUpdates(); Exception? startupException = null; Exception? loginException = null; ValidationResult? 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"); FormUtils.Register(); Logger.Send(LogType.Information, "", "Registering Classes"); progress.Report("Registering Classes"); var tasks = new List { Task.Run(() => CoreUtils.RegisterClasses(typeof(KanbanGrid).Assembly)), Task.Run(() => CoreUtils.RegisterClasses()), Task.Run(() => ComalUtils.RegisterClasses()), Task.Run(() => ReportUtils.RegisterClasses()), Task.Run(() => ConfigurationUtils.RegisterClasses()), Task.Run(() => 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()); 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 == null && App.DatabaseSettings.Autologin) { progress.Report("Logging in..."); if (App.DatabaseSettings.LoginType == LoginType.UserID) { ValidationResult? result; try { result = ClientFactory.Validate(App.DatabaseSettings.UserID, App.DatabaseSettings.Password); } catch(Exception e) { Logger.Send(LogType.Error, ClientFactory.UserID, $"Error connecting to server: {CoreUtils.FormatException(e)}"); loginException = new Exception("Error connecting to server.\nPlease check the server URL and port number."); result = null; } loginStatus = result; if (result == ValidationResult.INVALID) { loginException = new Exception("Unable to Login with User ID: " + App.DatabaseSettings.UserID); } } if(loginStatus == ValidationResult.VALID) { LoadCurrentEmployee(); if(!CheckTimesheetBypass(false)) { loginException = new Exception("You must clock on before logging in to PRS!"); loginStatus = null; } } }*/ }); if (startupException != null) MessageBox.Show(startupException.Message); else if (loginException != null) MessageBox.Show(loginException.Message); if (DoLogin(App.DatabaseSettings.Autologin) == ValidationResult.VALID) { AfterLogin(); } ProfileName.Content = App.Profile; URL.Content = DatabaseType switch { DatabaseType.Networked => ClientFactory.Parameters?.FirstOrDefault(), DatabaseType.Standalone => App.DatabaseSettings.FileName, DatabaseType.Local => App.DatabaseSettings.LocalServerName, _ => "" }; Progress.ShowModal("Starting Up", progress => { if (loginStatus == ValidationResult.VALID && DatabaseType == DatabaseType.Standalone) { progress.Report("Starting Scheduler"); scheduler.Start(); } }); } 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; BaseDynamicGrid.SelectionBackground = ThemeManager.SelectionBackgroundBrush; BaseDynamicGrid.SelectionForeground = ThemeManager.SelectionForegroundBrush; BaseDynamicGrid.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(); CurrentPanel?.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 interface ISetupActionItem { } private class SetupActionItem : ISetupActionItem { public string Name { get; set; } public Action Action { get; set; } public Bitmap? Image { get; set; } public Type? SecurityToken { get; set; } public Func? CanView { get; set; } public SetupActionItem(string name, Action action, Bitmap? image) { Name = name; Action = action; Image = image; } public bool IsVisible() => (SecurityToken == null || Security.IsAllowed(SecurityToken)) && (CanView == null || CanView()); public SetupActionItem SetSecurityToken() where T : ISecurityDescriptor, new() { SecurityToken = typeof(T); return this; } public SetupActionItem SetCanView() where T : Entity { return SetSecurityToken>>(); } } private class SetupSeparator : ISetupActionItem { } private Dictionary> SetupActions = new(); private List GetSetupActionList(Fluent.RibbonTabItem tab) { if (!SetupActions.TryGetValue(tab, out var list)) { list = new(); SetupActions[tab] = list; } return list; } private SetupActionItem AddSetupAction(List list, string header, Action action, Bitmap? image) { var item = new SetupActionItem(header, action, image); list.Add(item); return item; } private SetupActionItem AddSetupAction(Fluent.RibbonTabItem tab, string header, Action action, Bitmap? image) => AddSetupAction(GetSetupActionList(tab), header, action, image); private SetupActionItem? AddSetupAction(Fluent.RibbonTabItem tab, string header, Action action, Bitmap? image, bool canView) { if (!canView) return null; return AddSetupAction(tab, header, action, image); } private SetupSeparator AddSetupSeparator(List list) { var separator = new SetupSeparator(); list.Add(separator); return separator; } private SetupSeparator AddSetupSeparator(Fluent.RibbonTabItem tab) => AddSetupSeparator(GetSetupActionList(tab)); private void AddSetupModulesAndReports(Fluent.RibbonTabItem tab) { AddSetupSeparator(tab); AddSetupAction(tab, "Custom Modules", ManageModules, PRSDesktop.Resources.script).SetSecurityToken(); AddSetupAction(tab, "Reports", ManageReports, PRSDesktop.Resources.printer).SetSecurityToken(); AddSetupAction(tab, "Email Templates", ManageEmailTemplates, PRSDesktop.Resources.email).SetSecurityToken(); } private void ConfigureMainScreen() { 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); var bMaps = Security.CanView() || Security.CanView() || Security.CanView() || Security.CanView(); Progress.ShowModal( new ProgressSection( "Configuring Main Screen", () => { ReportUtils.ExportDefinitions.Clear(); ReportUtils.ExportDefinitions.AddRange(AddTemplateDefinitions()); //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; } } } ), new ProgressSection( "Configuring Quotes", () => { //// Quotes Menu Bar SetVisibility(QuotesDashboardButton, Security.IsAllowed()); SetVisibility(QuotesMessagesButton, Security.CanView()); SetVisibility(QuotesTaskButton, Security.IsAllowed()); SetVisibility(QuotesAttendanceButton, Security.IsAllowed()); SetVisibility(QuotesMapButton, bMaps); SetVisibility(QuotesDailyReportButton, ClientFactory.IsSupported() && Security.IsAllowed()); SetVisibility(QuotesButton, Security.CanView()); SetVisibility(KitsMasterList, Security.CanView()); SetVisibility(CostSheetsMasterList, Security.CanView()); SetVisibleIfEither(QuotesTaskSeparator, new FrameworkElement[] { QuotesDashboardButton, QuotesMessagesButton, QuotesTaskButton, QuotesAttendanceButton, QuotesMapButton, QuotesDailyReportButton }, new FrameworkElement[] { QuotesButton }); SetVisibleIfEither(QuotesActionSeparator, new FrameworkElement[] { QuotesButton }, new FrameworkElement[] { KitsMasterList, CostSheetsMasterList }); SetVisibleIfAny(QuotesActions, QuotesButton, KitsMasterList, CostSheetsMasterList); AddSetupAction(QuotesTab, "Status Codes", QuoteStatusButton_Click, PRSDesktop.Resources.quotestatus, ClientFactory.IsSupported() && Security.IsAllowed()); AddSetupAction(QuotesTab, "Design Sections", QuoteDesignSectionButton_Click, PRSDesktop.Resources.design, Security.CanView()); AddSetupSeparator(QuotesTab); AddSetupAction(QuotesTab, "Kit Conditions", KitConditionList_Click, PRSDesktop.Resources.kitcondition, Security.CanView()); AddSetupAction(QuotesTab, "Kit Formulae", KitFormulaeList_Click, PRSDesktop.Resources.kitformula, Security.CanView()); AddSetupAction(QuotesTab, "Cost Sheet Types", CostSheetTypeList_Click, PRSDesktop.Resources.costsheettype, Security.CanView()); AddSetupAction(QuotesTab, "Cost Sheet Brands", CostSheetBrandList_Click, PRSDesktop.Resources.costsheetbrand, Security.CanView()); AddSetupAction(QuotesTab, "Cost Sheet Sections", CostSheetSectionList_Click, PRSDesktop.Resources.costsheetsection, Security.CanView()); AddSetupSeparator(QuotesTab); AddSetupAction(QuotesTab, "Symbols", QuoteDiagramSymbols_Checked, PRSDesktop.Resources.pencil, Security.CanView()); AddSetupAction(QuotesTab, "Symbol Types", QuoteDiagramSymbolTypes_Checked, PRSDesktop.Resources.attachment, Security.CanView()); AddSetupAction(QuotesTab, "Dimension Types", QuoteTakeOffUnits_Click, PRSDesktop.Resources.box, Security.CanView()); AddSetupSeparator(QuotesTab); AddSetupAction(QuotesTab, "Spreadsheet Templates", () => ViewSpreadsheetTemplates(), PRSDesktop.Resources.box, Security.CanView()); AddSetupModulesAndReports(QuotesTab); SetTabVisibleIfAny(QuotesTab, QuotesActions); } ), new ProgressSection( "Configuring Projects", () => { 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()); SetVisibility(ServiceButton, false); SetVisibility(ProjectPlannerButton, Security.CanView()); SetVisibleIfEither(ProjectTaskSeparator, new FrameworkElement[] { ProjectsDashboardButton, ProjectMessagesButton, ProjectTaskButton, ProjectAttendanceButton, ProjectsMapButton, ProjectDailyReportButton }, new FrameworkElement[] { QuotesButton, ProjectsButton, ServiceButton, ProjectPlannerButton }); AddSetupAction(ProjectsTab, "Job Statuses", JobStatusButton_Click, PRSDesktop.Resources.view, Security.CanView()); AddSetupAction(ProjectsTab, "Document MileStones", JobDocumentMileStoneButton_OnClick, PRSDesktop.Resources.revision, true); //AddSetupAction(ProjectsTab, "Document Tags", JobDocumentTagButton_OnClick, PRSDesktop.Resources.checklist, // true); AddSetupAction(ProjectsTab, "Financial Statuses", FinancialStatusButton_Click, PRSDesktop.Resources.view, Security.CanView()); AddSetupAction(ProjectsTab, "Drawing Templates", DrawingTemplatesButton_Click, PRSDesktop.Resources.doc_misc, true); AddSetupSeparator(ProjectsTab); AddSetupAction(ProjectsTab, "Spreadsheet Templates", () => ViewSpreadsheetTemplates(), PRSDesktop.Resources.box, Security.CanView()); AddSetupModulesAndReports(ProjectsTab); //ProjectsActions.IsLauncherButtonVisible = Security.IsAllowed(); //ProjectReports.IsLauncherButtonVisible = Security.IsAllowed(); SetTabVisibleIfAny(ProjectsTab, ProjectsButton, ServiceButton, ProjectPlannerButton); } ), new ProgressSection( "Configuring Manufacturing", () => { 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()); SetVisibility(FactoryStatusButton, ClientFactory.IsSupported() && Security.IsAllowed()); SetVisibility(FactoryAllocationButton, ClientFactory.IsSupported() && Security.IsAllowed()); //SetVisibility(FactoryScheduleButton, ClientFactory.IsSupported() && ClientFactory.IsEnabled<>()); SetVisibility(FactoryFloorButton, ClientFactory.IsSupported() && Security.IsAllowed()); //SetVisibility(FactoryReadyButton, ClientFactory.IsSupported() && Security.IsAllowed()); SetVisibleIfEither(ManufacturingTaskSeparator, new FrameworkElement[] { ManufacturingDashboardButton, ManufacturingMessagesButton, ManufacturingTaskButton, ManufacturingAttendanceButton, ManufacturingMapButton, ManufacturingDailyReportButton }, new FrameworkElement[] { FactoryStatusButton, FactoryAllocationButton, FactoryFloorButton /* , FactoryReadyButton */ }); SetVisibleIfAny(ManufacturingActions, ManufacturingDashboardButton, ManufacturingMessagesButton, ManufacturingTaskButton, ManufacturingAttendanceButton, ManufacturingDailyReportButton, FactoryStatusButton, FactoryAllocationButton, FactoryFloorButton); AddSetupAction(ManufacturingTab, "Factory Settings", FactorySetup_Click, PRSDesktop.Resources.factorysetup, ClientFactory.IsSupported() && Security.IsAllowed()); AddSetupAction(ManufacturingTab, "Manufacturing Templates", TemplateSetup_Click, PRSDesktop.Resources.template, Security.CanView()); AddSetupAction(ManufacturingTab, "Manufacturing Trolleys", TrolleySetup_Click, PRSDesktop.Resources.trolley, Security.CanView()); AddSetupAction(ManufacturingTab, "Lost Time Types", LostTimeSetup_Click, PRSDesktop.Resources.smiley, Security.CanView()); //ManufacturingActions.IsLauncherButtonVisible = Security.IsAllowed(); //ManufacturingReports.IsLauncherButtonVisible = Security.IsAllowed(); AddSetupModulesAndReports(ManufacturingTab); SetTabVisibleIfAny(ManufacturingTab, FactoryStatusButton, FactoryAllocationButton, FactoryFloorButton); } ), new ProgressSection( "Configuring Logistics", () => { 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()); SetVisibility(DispatchButton, Security.CanView() && Security.CanView()); SetVisibility(RequisitionsButton, Security.CanView()); SetVisibility(DeliveriesButton, Security.IsAllowed()); SetVisibility(DeliveredItemsButton, ClientFactory.IsSupported() && Security.IsAllowed()); SetVisibility(ConsignmentButton, Security.CanView()); 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 }); AddSetupAction(LogisticsTab, "Delivery Types", DeliveryTypesButton_Click, PRSDesktop.Resources.truck, Security.CanView()); AddSetupAction(LogisticsTab, "Consignment Types", ConsignmentTypesButton_Click, PRSDesktop.Resources.service, Security.CanView()); //LogisticsActions.IsLauncherButtonVisible = Security.IsAllowed(); //LogisticsReports.IsLauncherButtonVisible = Security.IsAllowed(); AddSetupModulesAndReports(LogisticsTab); SetTabVisibleIfAny(LogisticsTab, DispatchButton, RequisitionsButton, DeliveriesButton, ReadyToGoItemsButton, DeliveredItemsButton, ConsignmentButton); } ), new ProgressSection( "Configuring Products", () => { 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()); SetVisibility(StockLocationList, Security.CanView()); SetVisibility(StockMovementList, Security.CanView()); SetVisibility(StockSummaryButton, Security.CanView() && Security.CanView()); SetVisibleIfEither(ProductsTaskSeparator, new FrameworkElement[] { ProductsDashboardButton, ProductsMessagesButton, ProductsTaskButton, ProductsAttendanceButton, ProductsMapButton, ProductsDailyReportButton }, new FrameworkElement[] { ProductsMasterList, StockLocationList, StockMovementList, StockSummaryButton }); SetVisibleIfAny(ProductActions, ProductsMasterList, StockLocationList, StockMovementList, StockSummaryButton); AddSetupAction(ProductTab, "Product Dimensions", ProductDimensionUnitsList_Click, PRSDesktop.Resources.unitofmeasure, Security.CanView()); AddSetupAction(ProductTab, "Product Groups", ProductGroupsList_Click, PRSDesktop.Resources.productgroup, Security.CanView()); AddSetupAction(ProductTab, "Product Styles", ProductStylesList_Click, PRSDesktop.Resources.palette, Security.CanView()); AddSetupAction(ProductTab, "Stock Areas", StockAreasList_Click, PRSDesktop.Resources.rack, Security.CanView()); AddSetupAction(ProductTab, "Stock Warehouses", StockWarehouseList_Click, PRSDesktop.Resources.factorysetup, Security.CanView()); AddSetupSeparator(ProductTab); AddSetupAction(ProductTab, "Spreadsheet Templates", () => ViewSpreadsheetTemplates(), PRSDesktop.Resources.box, Security.CanView()); AddSetupModulesAndReports(ProductTab); SetTabVisibleIfAny(ProductTab, ProductActions); } ), new ProgressSection( "Configuring Human Resources", () => { 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(AssignmentsButton, Security.CanView()); SetVisibility(EmployeePlannerButton, Security.CanView()); SetVisibility(TimesheetsButton, Security.CanView()); SetVisibility(LeaveRequestsButton, Security.CanView()); SetVisibility(OrgChartButton, ClientFactory.IsSupported() && ( Security.IsAllowed() || Security.IsAllowed() || Security.IsAllowed() ) ); SetVisibleIfEither(HumanResourcesTaskSeparator, new FrameworkElement[] { HumanResourcesDashboardButton, HumanResourcesMessagesButton, HumanResourcesTaskButton, HumanResourcesAttendanceButton, HumanResourcesMapButton, HumanResourcesDailyReportButton }, new FrameworkElement[] { AssignmentsButton, EmployeePlannerButton, TimesheetsButton, LeaveRequestsButton, OrgChartButton }); SetVisibleIfAny(HumanResourcesActions, HumanResourcesDashboardButton, HumanResourcesTaskButton, HumanResourcesAttendanceButton, HumanResourcesDailyReportButton, AssignmentsButton, EmployeePlannerButton, TimesheetsButton, LeaveRequestsButton, OrgChartButton); SetVisibility(UsersButton, Security.CanView()); SetVisibility(EmployeesButton, Security.CanView()); AddSetupAction(HumanResourcesTab, "Security Groups", SecurityGroupsButton_Click, PRSDesktop.Resources.securitygroup, Security.CanView()); AddSetupSeparator(HumanResourcesTab); AddSetupAction(HumanResourcesTab, "Employee Groups", GroupsSetup_Click, PRSDesktop.Resources.employees, Security.CanView()); AddSetupAction(HumanResourcesTab, "Positions", PositionsSetup_Click, PRSDesktop.Resources.position, Security.CanView()); AddSetupAction(HumanResourcesTab, "Roles", RolesSetup_Click, PRSDesktop.Resources.employeerole, Security.CanView()); AddSetupAction(HumanResourcesTab, "Teams", EmployeeTeamsButton_Click, PRSDesktop.Resources.team, Security.CanView()); AddSetupAction(HumanResourcesTab, "Activities", ActivityMenu_Click, PRSDesktop.Resources.quality, Security.CanView()); AddSetupAction(HumanResourcesTab, "Qualifications", QualificationMenu_Click, PRSDesktop.Resources.certificate, Security.CanView()); AddSetupAction(HumanResourcesTab, "Rosters", RosterMenu_Click, PRSDesktop.Resources.assignments, Security.CanView()); AddSetupSeparator(HumanResourcesTab); // AddSetupAction(HumanResourcesTab, "Rosters", RostersButton_Click, PRSDesktop.Resources.attendance, // Security.CanView()); AddSetupAction(HumanResourcesTab, "Overtime Rules", OvertimeRulesButton_Click, PRSDesktop.Resources.overtime, Security.CanView()); AddSetupAction(HumanResourcesTab, "Standard Leave", StandardLeaveButton_Click, PRSDesktop.Resources.fireworks, Security.CanView()); SetVisibleIfEither(HumanResourcesSetupSeparator1, new FrameworkElement[] { AssignmentsButton, TimesheetsButton, LeaveRequestsButton, OrgChartButton }, new FrameworkElement[] { UsersButton, EmployeesButton }); //HumanResourcesActions.IsLauncherButtonVisible = Security.IsAllowed(); //HumanResourcesReports.IsLauncherButtonVisible = Security.IsAllowed(); AddSetupSeparator(HumanResourcesTab); AddSetupAction(HumanResourcesTab, "Spreadsheet Templates", () => ViewSpreadsheetTemplates(), PRSDesktop.Resources.box, Security.CanView()); AddSetupModulesAndReports(HumanResourcesTab); SetTabVisibleIfAny(HumanResourcesTab, AssignmentsButton, TimesheetsButton, LeaveRequestsButton, OrgChartButton, UsersButton, EmployeesButton); } ), new ProgressSection( "Configuring Accounts", () => { SetVisibility(AccountsDashboardButton, Security.IsAllowed()); SetVisibility(AccountsMessagesButton, Security.CanView()); SetVisibility(AccountsTaskButton, ClientFactory.IsSupported() && Security.IsAllowed()); SetVisibility(AccountsDataButton, Security.IsAllowed()); SetVisibility(AccountsAttendanceButton, ClientFactory.IsSupported() && Security.IsAllowed()); SetVisibility(AccountsMapButton, bMaps); SetVisibility(AccountsDailyReportButton, ClientFactory.IsSupported() && Security.IsAllowed()); SetVisibility(CustomerList, ClientFactory.IsSupported() && Security.CanView()); SetVisibility(InvoiceList, ClientFactory.IsSupported() && Security.CanView()); SetVisibility(ReceiptList, ClientFactory.IsSupported() && Security.CanView()); SetVisibility(SupplierList, ClientFactory.IsSupported() && Security.CanView()); SetVisibility(PurchasesList, ClientFactory.IsSupported() && Security.CanView()); SetVisibility(BillsList, ClientFactory.IsSupported() && Security.CanView()); SetVisibility(PaymentsList, ClientFactory.IsSupported() && Security.CanView()); 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, PurchasesList, BillsList, PaymentsList }); SetVisibleIfAny(AccountsActions, AccountsDashboardButton, AccountsMessagesButton, AccountsTaskButton, AccountsAttendanceButton, AccountsDailyReportButton, CustomerList, InvoiceList, ReceiptList, SupplierList, PurchasesList, BillsList, PaymentsList); AddSetupAction(AccountsTab, "Contact Types", ContactTypeList_Click, PRSDesktop.Resources.contacttype, ClientFactory.IsSupported() && Security.CanView()); AddSetupAction(AccountsTab, "Tax Codes", TaxCodeList_Click, PRSDesktop.Resources.taxcode, ClientFactory.IsSupported() && Security.CanView()); AddSetupAction(AccountsTab, "Receipt Types", ReceiptTypeList_Click, PRSDesktop.Resources.receipt, ClientFactory.IsSupported() && Security.CanView()); AddSetupAction(AccountsTab, "Payment Types", PaymentTypeList_Click, PRSDesktop.Resources.payment, ClientFactory.IsSupported() && Security.CanView()); AddSetupAction(AccountsTab, "Cost Centres", CostCentresList_Click, PRSDesktop.Resources.costcentre, ClientFactory.IsSupported() && Security.CanView()); AddSetupAction(AccountsTab, "GL Codes", GLCodesList_Click, PRSDesktop.Resources.glcode, ClientFactory.IsSupported() && Security.CanView()); AddSetupAction(AccountsTab, "Purchase Order Categories", PurchaseOrderCategoriesButton_Click, PRSDesktop.Resources.service, ClientFactory.IsSupported() && Security.CanView()); //AccountsActions.IsLauncherButtonVisible = Security.IsAllowed(); //AccountsReports.IsLauncherButtonVisible = Security.IsAllowed(); AddSetupSeparator(AccountsTab); AddSetupAction(AccountsTab, "Customer Spreadsheet Templates", () => ViewSpreadsheetTemplates(), PRSDesktop.Resources.box, Security.CanView()); AddSetupAction(AccountsTab, "Supplier Spreadsheet Templates", () => ViewSpreadsheetTemplates(), PRSDesktop.Resources.box, Security.CanView()); AddSetupModulesAndReports(AccountsTab); SetTabVisibleIfAny(AccountsTab, CustomerList, InvoiceList, ReceiptList, SupplierList, PurchasesList, BillsList, PaymentsList); } ), new ProgressSection( "Configuring Equipment", () => { 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()); SetVisibleIfEither(EquipmentTaskSeparator, new FrameworkElement[] { EquipmentDashboardButton, EquipmentMessagesButton, EquipmentTaskButton, EquipmentAttendanceButton, EquipmentMapButton, EquipmentDailyReportButton }, new FrameworkElement[] { EquipmentButton }); SetVisibleIfAny(EquipmentActions, EquipmentDashboardButton, EquipmentMessagesButton, EquipmentTaskButton, EquipmentAttendanceButton, EquipmentDailyReportButton, EquipmentButton); SetVisibility(TrackersMasterList, Security.CanView()); AddSetupAction(EquipmentTab, "Tracker Types", TrackerTypes_Click, PRSDesktop.Resources.milestone, Security.CanView()); AddSetupAction(EquipmentTab, "Stickers", Stickers_Click, PRSDesktop.Resources.barcode, Security.CanView()); AddSetupAction(EquipmentTab, "Digital Keys", DigitalKeys_Click, PRSDesktop.Resources.key, Security.CanView()); AddSetupAction(EquipmentTab, "Equipment Groups", EquipmentGroupList_Click, PRSDesktop.Resources.specifications, Security.CanView()); //EquipmentActions.IsLauncherButtonVisible = Security.IsAllowed(); //EquipmentReports.IsLauncherButtonVisible = Security.IsAllowed(); AddSetupModulesAndReports(EquipmentTab); SetTabVisibleIfAny(EquipmentTab, EquipmentButton, TrackersMasterList); } ), new ProgressSection( "Configuring Dashboards", () => { 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()); SetVisibility(TemplateAnalysisButton, ClientFactory.IsSupported() && Security.IsAllowed()); SetVisibility(FactoryAnalysisButton, ClientFactory.IsSupported() && Security.IsAllowed()); SetVisibility(DatabaseActivityButton, ClientFactory.IsSupported() && Security.IsAllowed()); SetVisibility(UserActivityButton, ClientFactory.IsSupported() && Security.IsAllowed()); SetVisibility(QAAnalysisButton, Security.IsAllowed()); SetVisibility(QuickStatusButton, Security.IsAllowed()); SetVisibleIfEither(DashboardsTaskSeparator, new FrameworkElement[] { DashboardsDashboardButton, DashboardMessagesButton, DashboardsTaskButton, DashboardsAttendanceButton, DashboardsMapButton, DashboardsDailyReportButton }, new FrameworkElement[] { FactoryProductivityButton, TemplateAnalysisButton, FactoryAnalysisButton, DatabaseActivityButton, UserActivityButton, QAAnalysisButton, QuickStatusButton }); SetVisibleIfAny(DashboardsActions, DashboardsDashboardButton, DashboardMessagesButton, DashboardsTaskButton, DashboardsAttendanceButton, DashboardsDailyReportButton, FactoryProductivityButton, TemplateAnalysisButton, FactoryAnalysisButton, DatabaseActivityButton, UserActivityButton, QAAnalysisButton, QuickStatusButton); //DashboardsActions.IsLauncherButtonVisible = Security.IsAllowed(); //DashboardsReports.IsLauncherButtonVisible = Security.IsAllowed(); AddSetupModulesAndReports(DashboardsTab); SetVisibleIfAny(DashboardsTab, FactoryProductivityButton, TemplateAnalysisButton, FactoryAnalysisButton, DatabaseActivityButton, UserActivityButton, QAAnalysisButton, QuickStatusButton); } ), new ProgressSection( "Configuring System Modules", () => { 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()); SetVisibility(DocumentList, ClientFactory.IsSupported() && Security.IsAllowed()); SetVisibility(QAFormSetupButton, ClientFactory.IsSupported() && Security.IsAllowed()); SetVisibleIfAny(BackstageSeparator2, DocumentTypeList, DocumentList, QAFormSetupButton); 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(ScansDock, Scans); _ribbon.InvalidateArrange(); } ) ); } private void ViewSpreadsheetTemplates() where TEntity : Entity { SpreadsheetTemplateGrid grid = new SpreadsheetTemplateGrid(); grid.AppliesTo = typeof(TEntity); grid.Refresh(true, true); DynamicContentDialog dlg = new DynamicContentDialog(grid,false); dlg.Title = $"{typeof(TEntity).EntityName().Split(".").Last()} Spreadsheet Templates"; dlg.ShowDialog(); } private IEnumerable AddTemplateDefinitions() { if (CurrentPanel == null) return new List() { new ReportExportDefinition("Email Report", PRSDesktop.Resources.email, ReportExportType.PDF, EmailUtils.DoEmailReport)}; else return EmailUtils.CreateTemplateDefinitions(CurrentPanel.DataModel(Selection.None)); } 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)) { MessageBox.Show("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); dailyReportPanel.OnTimeSheetConfirmed += e => { if (!OutstandingDailyReports(true)) { ConfigureMainScreen(); 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) { MessageBox.Show(string.Format("Unable to Load {0}!\n\n{1}\n{2}", app.Settings["CurrentPanel"], e.Message, e.StackTrace)); } } } 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 T LoadWindow(Fluent.Button sender) where T : IBasePanel, new() { using (new WaitCursor()) { UnloadWindow(); CurrentTab = GetTabItem(sender); CurrentButton = sender; //CurrentButton.IsSelected = true; UpdateRibbonColors(); var panel = new T(); CurrentPanel = panel; ReportUtils.ExportDefinitions.Clear(); ReportUtils.ExportDefinitions.AddRange(AddTemplateDefinitions()); InitializePanelProperties(panel); CurrentPanel.IsReady = false; CurrentPanel.Setup(); CurrentPanel.IsReady = true; CurrentPanel.OnUpdateDataModel += (s, m) => { ReloadModules(s, m); ReloadReports(s, m); }; CurrentPanel_Label = sender.Header?.ToString() ?? ""; CurrentPanel_Ticks = DateTime.Now; ContentControl.Content = CurrentPanel; Title = $"{CurrentPanel_Label} - {(String.Equals(App.Profile?.ToUpper(), "DEFAULT") ? "PRS Desktop" : App.Profile)} (Release {CoreUtils.GetVersion()})"; if (sender != null) { var model = CurrentPanel.DataModel(Selection.None); var section = CurrentPanel.SectionName; ReloadModules(section, model); ReloadReports(section, model); } CurrentPanel.Refresh(); if (CurrentPanel 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)); } } } //stopwatch.Stop(); //Logger.Send(LogType.Information, ClientFactory.UserID, String.Format("Loading {0} to took {1} ms", sender != null ? sender.Label : module, stopwatch.ElapsedMilliseconds)); 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(DocumentStore).Assembly, typeof(EquipmentStore).Assembly }, myType => myType.IsClass && !myType.IsAbstract && !myType.IsGenericType && myType.GetInterfaces().Contains(typeof(IStore)) ).ToArray(); DbFactory.Provider = new SQLiteProvider(App.DatabaseSettings.FileName); DbFactory.ColorScheme = App.DatabaseSettings.ColorScheme; DbFactory.Logo = App.DatabaseSettings.Logo; progress.Report("Starting Local Database"); DbFactory.Start(); progress.Report("Checking Database"); var users = DbFactory.Provider.Load(); if (!users.Any()) { var user = new User { UserID = "ADMIN", Password = "admin" }; DbFactory.Provider.Save(user); var employee = DbFactory.Provider.Load(new Filter(x => x.Code).IsEqualTo("ADMIN")).FirstOrDefault(); employee ??= new Employee { Code = "ADMIN", Name = "Administrator Account" }; employee.UserLink.ID = user.ID; DbFactory.Provider.Save(employee); } StoreUtils.GoogleAPIKey = App.DatabaseSettings.GoogleAPIKey; JobStore.AutoIncrementPrefix = App.DatabaseSettings.JobPrefix; PurchaseOrderStore.AutoIncrementPrefix = App.DatabaseSettings.PurchaseOrderPrefix; } #endregion #region Login Management private ValidationResult? DoLogin(bool autoLogin) { ValidationResult? result = null; if (autoLogin) { if (App.DatabaseSettings.LoginType == LoginType.UserID) { try { result = ClientFactory.Validate(App.DatabaseSettings.UserID, App.DatabaseSettings.Password); } catch (Exception e) { Logger.Send(LogType.Error, ClientFactory.UserID, $"Error connecting to server: {CoreUtils.FormatException(e)}"); MessageBox.Show("Error connecting to server.\nPlease check the server URL and port number."); result = null; } if (result == ValidationResult.INVALID) { MessageBox.Show("Unable to Login with User ID: " + App.DatabaseSettings.UserID); } } } if (result != ValidationResult.VALID) { var login = new PinLogin(CoreUtils.GetVersion(), result ?? ValidationResult.INVALID); if (login.ShowDialog() == true) { result = ValidationResult.VALID; } } return result ?? ValidationResult.INVALID; } /// /// To be called after and if a valid login session exists. Configures the main screen and loads the windows. /// private void AfterLogin() { LoadCurrentEmployee(); if (CheckTimesheetBypass(true)) { UpdateCurrentLogin(); } else { MessageBox.Show("You must clock on before logging in to PRS!"); ClientFactory.InvalidateUser(); App.EmployeeID = Guid.Empty; App.EmployeeName = ""; App.EmployeeEmail = ""; } ApplyColorScheme(); ConfigureMainScreen(); LoadApplicationState(); 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), new Columns(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.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(); 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(); LoadSecondaryWindows(); if (message != null) { MessageBox.Show(message); } if (DoLogin(false) == ValidationResult.VALID) { AfterLogin(); } 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, new Columns( 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()); } #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() { if (CurrentPanel != null) { Heartbeat(DateTime.Now - CurrentPanel_Ticks, true); try { CurrentPanel.Shutdown(); } catch (Exception e) { Logger.Send(LogType.Error, ClientFactory.UserID, string.Format("Error in UnloadWindow(): {0}\n{1}", e.Message, e.StackTrace)); } CurrentPanel_Ticks = DateTime.MinValue; CurrentPanel_Label = ""; CurrentPanel_Clicks = 0; CurrentPanel_Keys = 0; Title = $"{CurrentPanel_Label} - {(String.Equals(App.Profile?.ToUpper(), "DEFAULT") ? "PRS Desktop" : App.Profile)} (Release {CoreUtils.GetVersion()})"; if (CurrentTab != 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; CurrentPanel = null; ContentControl.Content = null; } private void SecondaryWindow_Click(object sender, RoutedEventArgs e) { if (CurrentPanel == null) return; var id = Guid.NewGuid(); var window = new Tuple( CurrentPanel.GetType().EntityName(), CurrentPanel_Label, 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); }); //foreach (var item in _ribbon.BackStage.Items) //{ // BackStageCommandButton button = item as BackStageCommandButton; // if ((button != null) && (button.Header != "Log Out") && (button.Header != "Exit")) // SecurityFactory.Register("System", button.Header, String.Format("System: Configure {0}", button.Header)); //} } 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 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 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 void SetTabVisibleIfAny(Fluent.RibbonTabItem tab, params FrameworkElement[] buttons) { var bVisible = false; foreach (var button in buttons) bVisible = bVisible || button.Visibility == Visibility.Visible; bVisible = bVisible || (SetupActions.GetValueOrDefault(tab)?.Count(x => x is SetupActionItem) ?? 0) > 3; tab.Visibility = bVisible ? Visibility.Visible : Visibility.Collapsed; } private 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; } //private Style tabselected = null; //private Style tabunselected = null; //private Style Getstyle(bool selected) //{ // if (tabunselected == null) // { // tabunselected = new Style(typeof(TabButton)); // Setter st1 = new Setter(TabButton.BackgroundProperty, new SolidColorBrush(Colors.Transparent)); // tabunselected.Setters.Add(st1); // } // if (tabselected == null) // { // tabselected = new Style(typeof(TabButton)); // Setter st1 = new Setter(TabButton.BackgroundProperty, new SolidColorBrush(System.Windows.Media.Color.FromArgb(0xFF, 0x9E, 0xDA, 0x11))); // tabselected.Setters.Add(st1); // } // return selected ? tabselected : tabunselected; //} #region Button Event Handlers private void DataEntry_Click(object sender, RoutedEventArgs e) { LoadWindow((Fluent.Button)sender); } private void Console_Click(object sender, RoutedEventArgs a) { if (_console is null) { _console = new Console(); _console.Closing += (o, args) => _console = null; } _console.Show(); } private void Quotes_Checked(object sender, RoutedEventArgs e) { LoadWindow((Fluent.Button)sender); } 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 Service_Checked(object sender, RoutedEventArgs e) { MessageBox.Show("Not Implemented"); //LoadWindow((RibbonButton)sender); } private void Tasks_Checked(object sender, RoutedEventArgs e) { //LoadWindow((RibbonButton)sender); LoadWindow((Fluent.Button)sender); } private void ManufacturingMenu_Checked(object sender, RoutedEventArgs e) { LoadWindow((Fluent.Button)sender); } // private void Schedule_Checked(object sender, RoutedEventArgs e) // { // LoadWindow((RibbonButton)sender); // } private void FactoryFloorButton_Click(object sender, RoutedEventArgs e) { LoadWindow((Fluent.Button)sender); } private void DispatchMenu_Checked(object sender, RoutedEventArgs e) { LoadWindow((Fluent.Button)sender); } private void ReadyToGoMenu_Checked(object sender, RoutedEventArgs e) { LoadWindow((Fluent.Button)sender); } private void Equipment_Checked(object sender, RoutedEventArgs e) { LoadWindow((Fluent.Button)sender); } private void Requisitions_Checked(object sender, RoutedEventArgs e) { LoadWindow((Fluent.Button)sender); } //private void Purchases_Checked(object sender, RoutedEventArgs e) //{ // LoadWindow(PurchasesMenu, new PurchasesPanel()); //} private void Timesheets_Checked(object sender, RoutedEventArgs e) { LoadWindow((Fluent.Button)sender); } private void Attendance_Checked(object sender, RoutedEventArgs e) { LoadWindow((Fluent.Button)sender); } private void Maps_Checked(object sender, RoutedEventArgs e) { LoadWindow((Fluent.Button)sender); } private void FactorySetup_Click() { var list = new MasterList(typeof(ManufacturingFactory)); list.ShowDialog(); } private void TemplateSetup_Click() { var list = new MasterList(typeof(ManufacturingTemplate), "Factory.Name", null, true); list.ShowDialog(); } private void RefreshMenu_Click(object sender, RoutedEventArgs e) { if (CurrentPanel != null) CurrentPanel.Refresh(); } private void MenuItem_Click(object sender, RoutedEventArgs e) { } private void QuoteStatusButton_Click() { var list = new MasterList(typeof(QuoteStatus)); list.ShowDialog(); } private void QuoteDesignSectionButton_Click() { var list = new MasterList(typeof(QuoteDesignSection)); list.ShowDialog(); } private void JobStatusButton_Click() { var list = new MasterList(typeof(JobStatus)); list.ShowDialog(); } private void FinancialStatusButton_Click() { var list = new MasterList(typeof(JobFinancialStatus)); list.ShowDialog(); } private void DrawingTemplatesButton_Click() { var list = new MasterList(typeof(DrawingTemplate)); list.ShowDialog(); } private void RolesSetup_Click() { var list = new MasterList(typeof(Role)); list.ShowDialog(); } private void GroupsSetup_Click() { var list = new MasterList(typeof(EmployeeGroup)); list.ShowDialog(); } private void PositionsSetup_Click() { var list = new MasterList(typeof(EmployeePosition)); list.ShowDialog(); } private void UserSetup_Click(object sender, RoutedEventArgs e) { LoadWindow((Fluent.Button)sender); //MasterList list = new MasterList(typeof(User)); //list.ShowDialog(); } private void Employees_Click(object sender, RoutedEventArgs e) { LoadWindow((Fluent.Button)sender); //MasterList list = new MasterList(typeof(Employee)); //list.ShowDialog(); } private void Customers_Click(object sender, RoutedEventArgs e) { var list = new MasterList(typeof(Customer)); list.ShowDialog(); } private void Stickers_Click() { var list = new MasterList(typeof(WebSticker)); list.ShowDialog(); } private void Trackers_Click(object sender, RoutedEventArgs e) { LoadWindow((Fluent.Button)sender); //MasterList list = new MasterList(typeof(GPSTracker)); //list.ShowDialog(); } private void TrackerTypes_Click() { var list = new MasterList(typeof(GPSTrackerType)); list.ShowDialog(); } private void DigitalKeys_Click() { var list = new MasterList(typeof(DigitalKey)); list.ShowDialog(); } private void SupplierMenu_Click(object sender, RoutedEventArgs e) { var list = new MasterList(typeof(Supplier)); list.ShowDialog(); } private void DeliveredOnSiteMenu_Checked(object sender, RoutedEventArgs e) { LoadWindow((Fluent.Button)sender); } private void DatabaseScripts_Click(object sender, RoutedEventArgs e) { var list = new MasterList(typeof(Script), "Section", null, true); list.ShowDialog(); } private void WebTemplates_Click(object sender, RoutedEventArgs e) { MasterList list = new MasterList(typeof(WebTemplate), null, null, true); list.ShowDialog(); } private void ActivityMenu_Click() { var list = new MasterList(typeof(Activity)); list.ShowDialog(); } private void QualificationMenu_Click() { var list = new MasterList(typeof(Qualification)); list.ShowDialog(); } private void RosterMenu_Click() { var list = new MasterList(typeof(EmployeeRoster)); list.ShowDialog(); } private void Reports_LauncherClick(object sender, RoutedEventArgs e) { } 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 StockSummaryButton_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 EquipmentGroupList_Click() { var list = new MasterList(typeof(EquipmentGroup)); list.ShowDialog(); } private void KitConditionList_Click() { var list = new MasterList(typeof(KitCondition)); list.ShowDialog(); } //private void KitGroupList_Click(object sender, RoutedEventArgs e) //{ // MasterList list = new MasterList(typeof(KitGroup)); // list.ShowDialog(); //} private void KitFormulaeList_Click() { var list = new MasterList(typeof(KitFormula)); list.ShowDialog(); } private void CustomFields_Click(object sender, RoutedEventArgs e) { var list = new MasterList(typeof(CustomProperty), "Class", null, true); list.ShowDialog(); } private void CostSheetTypeList_Click() { var list = new MasterList(typeof(CostSheetType)); list.ShowDialog(); } private void CostSheetBrandList_Click() { var list = new MasterList(typeof(CostSheetBrand)); list.ShowDialog(); } private void CostSheetSectionList_Click() { var list = new MasterList(typeof(CostSheetSection)); list.ShowDialog(); } private void ProductDimensionUnitsList_Click() { var list = new MasterList(typeof(ProductDimensionUnit)); list.ShowDialog(); } 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 ProductGroupsList_Click() { var list = new MasterList(typeof(ProductGroup)); list.ShowDialog(); } private void ProductStylesList_Click() { var list = new MasterList(typeof(ProductStyle)); list.ShowDialog(); } private void StockAreasList_Click() { var list = new MasterList(typeof(StockArea)); list.ShowDialog(); } private void CostCentresList_Click() { var list = new MasterList(typeof(CostCentre)); list.ShowDialog(); } private void GLCodesList_Click() { var list = new MasterList(typeof(GLCode)); list.ShowDialog(); } 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 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 ReceiptTypeList_Click() { var list = new MasterList(typeof(ReceiptType)); list.ShowDialog(); } private void PaymentTypeList_Click() { var list = new MasterList(typeof(PaymentType)); list.ShowDialog(); } private void TaxCodeList_Click() { var list = new MasterList(typeof(TaxCode)); list.ShowDialog(); } private void ContactTypeList_Click() { var list = new MasterList(typeof(ContactType)); list.ShowDialog(); } private void EmployeeTeamsButton_Click() { var list = new MasterList(typeof(Team)); list.ShowDialog(); } // private void RostersButton_Click() // { // var list = new MasterList(typeof(Roster)); // list.ShowDialog(); // } private void OvertimeRulesButton_Click() { var list = new MasterList(typeof(OvertimeRule)); list.ShowDialog(); } private void StandardLeaveButton_Click() { var list = new MasterList(typeof(StandardLeave)); list.ShowDialog(); } private void SecurityGroupsButton_Click() { var list = new MasterList(typeof(SecurityGroup)); list.ShowDialog(); Security.Reset(); } private void ImportDatabase_Click(object sender, RoutedEventArgs e) { Utility.DuplicateDatabase(); } private void DeliveriesButton_Click(object sender, RoutedEventArgs e) { LoadWindow((Fluent.Button)sender); } private void TaskTypesButton_OnClick(object sender, RoutedEventArgs e) { var list = new MasterList(typeof(KanbanType)); list.ShowDialog(); } private void TrolleySetup_Click() { var list = new MasterList(typeof(ManufacturingTrolley)); list.ShowDialog(); } private void LostTimeSetup_Click() { var list = new MasterList(typeof(ManufacturingLostTime)); list.ShowDialog(); } 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 QAAnalysisButton_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 ConsignmentTypesButton_Click() { var list = new MasterList(typeof(ConsignmentType)); list.ShowDialog(); } private void PurchaseOrderCategoriesButton_Click() { var list = new MasterList(typeof(PurchaseOrderCategory)); list.ShowDialog(); } private void DeliveryTypesButton_Click() { var list = new MasterList(typeof(DeliveryType)); list.ShowDialog(); } private void ConfigureModules_Click(object sender, RoutedEventArgs e) { var window = new ModuleConfigurationWindow(DbFactory.Provider.URL); if (window.ShowDialog() == true) MessageBox.Show("These changes will be applied when the database is restarted!"); } //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 }); } private void StockWarehouseList_Click() { var list = new MasterList(typeof(StockWarehouse)); list.ShowDialog(); } private void Dashboards_Checked(object sender, RoutedEventArgs e) { LoadWindow((Fluent.Button)sender); } private void QuoteDiagramSymbols_Checked() { var list = new MasterList(typeof(QuoteDiagramSymbol), "Section.Name"); list.ShowDialog(); QuoteDiagramSymbolCache.Refresh(); } private void QuoteDiagramSymbolTypes_Checked() { var list = new MasterList(typeof(QuoteDiagramSymbolSection)); list.ShowDialog(); QuoteDiagramSymbolCache.Refresh(); } private void QuoteTakeOffUnits_Click() { var list = new MasterList(typeof(QuoteTakeOffUnit)); list.ShowDialog(); } private void JobDocumentMileStoneButton_OnClick() { var list = new MasterList(typeof(JobDocumentSetMileStoneType)); list.ShowDialog(); } // private void JobDocumentTagButton_OnClick() // { // var list = new MasterList(typeof(JobDocumentSetTag)); // list.ShowDialog(); // } private void Setup_Click(object sender, RoutedEventArgs e) { var tab = _ribbon.SelectedTabItem; if (tab is null) return; var items = new List(); if (SetupActions.TryGetValue(tab, out var actions)) { foreach (var action in actions) { items.Add(action); } } if (PanelSetupActions.Any()) { AddSetupSeparator(items); foreach (var action in PanelSetupActions) { items.Add(action); } } items = items.Where(x => x is not SetupActionItem setup || setup.IsVisible()).ToList(); var menu = new ContextMenu(); for (var i = 0; i < items.Count; ++i) { var item = items[i]; if (item is SetupActionItem setupAction) { menu.AddItem(setupAction.Name, setupAction.Image, setupAction.Action); } else if (item is SetupSeparator && i > 0 && i < items.Count - 1) { var last = items[i - 1]; if (last is not SetupSeparator) menu.AddSeparator(); } } if (CurrentPanel?.GetType().HasInterface(typeof(IPropertiesPanel<>)) == true && Security.IsAllowed()) { menu.AddItem("Configure Panel", PRSDesktop.Resources.edit, ConfigurePanel_Click); } if (menu.Items.Count == 0) { menu.AddItem("No Items", null, null, false); } menu.IsOpen = true; } private void InitializePanelProperties(IBasePanel panel) { var propertiesInterface = panel.GetType().GetInterfaceDefinition(typeof(IPropertiesPanel<>)); if (propertiesInterface is not null) { var propertiesType = propertiesInterface.GenericTypeArguments[0]; var method = typeof(MainWindow) .GetMethod(nameof(InitializePanelPropertiesGeneric), BindingFlags.NonPublic | BindingFlags.Instance) ?.MakeGenericMethod(panel.GetType(), propertiesType) .Invoke(this, new object?[] { panel }); } } private void InitializePanelPropertiesGeneric(TPanel panel) where TPanel : IPropertiesPanel where TProperties : BaseObject, IGlobalConfigurationSettings, new() { panel.Properties = LoadPanelProperties(); } private TProperties LoadPanelProperties() where TPanel : IPropertiesPanel where TProperties : BaseObject, IGlobalConfigurationSettings, new() { var config = new GlobalConfiguration(); return config.Load(); } private void SavePanelProperties(TProperties properties) where TPanel : IPropertiesPanel where TProperties : BaseObject, IGlobalConfigurationSettings, new() { var config = new GlobalConfiguration(); config.Save(properties); } private void EditPanelProperties() where TPanel : IPropertiesPanel where TProperties : BaseObject, IGlobalConfigurationSettings, new() { var properties = LoadPanelProperties(); var editor = new DynamicEditorForm(typeof(TProperties)); editor.Items = new BaseObject[] { properties }; if (editor.ShowDialog() == true) { SavePanelProperties(properties); } } private void ConfigurePanel_Click() { if (CurrentPanel is null) return; var propertiesInterface = CurrentPanel.GetType().GetInterfaceDefinition(typeof(IPropertiesPanel<>))!; var propertiesType = propertiesInterface.GenericTypeArguments[0]; var method = typeof(MainWindow) .GetMethod(nameof(EditPanelProperties), BindingFlags.NonPublic | BindingFlags.Instance) ?.MakeGenericMethod(CurrentPanel.GetType(), propertiesType) .Invoke(this, Array.Empty()); } 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.Form.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.DefineFilter(Array.Empty()), new Filter(x => x.ID).InQuery( new Filter(x => x.Employee.ID).IsEqualTo(App.EmployeeID), x => x.Form.ID)), LookupFactory.DefineColumns() .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 Window_Unloaded(object sender, RoutedEventArgs e) { } //private bool _closingFromSystemMenu = false; private void Window_Closing(object sender, CancelEventArgs e) { /*if (!_closingFromSystemMenu && !CoreUtils.GetVersion().Equals("???")) { WindowState = WindowState.Minimized; e.Cancel = true; return; }*/ App.IsClosing = true; FinalizeAutoTimesheet(); if (!CoreUtils.GetVersion().Equals("???")) scheduler.Stop(); if (CurrentPanel != null) CurrentPanel.Shutdown(); CurrentPanel = null; CurrentTab = null; UpdateRibbonColors(); if (!CoreUtils.GetVersion().Equals("???")) if (station.ID != Guid.Empty) ExecuteLogout(); } 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) => { }); } } } } private void RibbonWindow_Closed(object sender, EventArgs e) { DisconnectRecorderNotes(); Application.Current.Shutdown(); } #region Notifications + Heartbeat private void ReceiveNotification(Notification notification) { if (Security.CanView()) { Notifications.AddNotification(notification); } if (CurrentPanel is NotificationPanel panel) { 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(DateTime.Now - CurrentPanel_Ticks, false); 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), new Columns(x => x.StationID), null, (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, CoreUtils.FormatException(remote)); } } else if (e is not null) { Logger.Send(LogType.Information, ClientFactory.UserID, 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(TimeSpan time, bool closing) { //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); } } //Logger.Send(LogType.Information, "", string.Format("Heartbeat: {0:hh\\:mm}{1}", time, closing ? " (closing)" : "")); if (!closing && time.TotalMinutes < 5) return; CurrentPanel_Ticks = DateTime.Now; if (CurrentPanel != null) { //Logger.Send(LogType.Information, "", string.Format("Heartbeat: {0}", CurrentPanel_Label)); if (ClientFactory.IsSupported()) { var keys = CurrentPanel_Keys; CurrentPanel_Keys = 0; var clicks = CurrentPanel_Clicks; CurrentPanel_Clicks = 0; var tracking = new ModuleTracking { Date = DateTime.Today, Module = CurrentPanel_Label, Clicks = clicks, Keys = keys, ActiveTime = clicks + keys > 0 ? time : new TimeSpan(), IdleTime = clicks + keys == 0 ? time : new TimeSpan() }; tracking.User.ID = ClientFactory.UserGuid; new Client().Save(tracking, "", (mt, ex) => { }); } CurrentPanel.Heartbeat(time); } } 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) { if (CurrentPanel != null) CurrentPanel_Clicks++; } private void RibbonWindow_PreviewKeyUp(object sender, KeyEventArgs e) { if (CurrentPanel != null) CurrentPanel_Keys++; } #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"); //, 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(); //private void ConvertProgressEvent(object sender, ConvertProgressEventArgs e) //{ // Dispatcher.Invoke(() => // { // try // { // if (e.TotalDuration.TotalSeconds != 0.0F) // Progress.SetMessage(String.Format("Transcoding ({0:F2}% complete)", e.ProcessedDuration.TotalSeconds * 100.0F / e.TotalDuration.TotalSeconds)); // } // catch (Exception err) // { // } // }); //} #region Check For Updates private string GetUpdateLocation() { if (App.DatabaseSettings.DatabaseType == DatabaseType.Networked) { //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).Last(); string url; 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 App.DatabaseSettings.DatabaseType switch { DatabaseType.Networked => Update.GetRemoteFile($"{location}/version").Content, DatabaseType.Standalone or DatabaseType.Local => File.ReadAllText(Path.Combine(location, "version.txt")), _ => "", }; } private string GetReleaseNotes(string location) { return App.DatabaseSettings.DatabaseType switch { DatabaseType.Networked => Update.GetRemoteFile($"{location}/releasenotes").Content, DatabaseType.Standalone or DatabaseType.Local => File.ReadAllText(Path.Combine(location, "Release Notes.txt")), _ => "", }; } private byte[]? GetInstaller(string location) { return App.DatabaseSettings.DatabaseType switch { DatabaseType.Networked => Update.GetRemoteFile($"{location}/install").RawBytes, DatabaseType.Standalone or DatabaseType.Local => File.ReadAllBytes(Path.Combine(location, "PRSDesktopSetup.exe")), _ => null, }; } private void CheckForUpdates() { Update.CheckForUpdates( GetUpdateLocation, GetLatestVersion, GetReleaseNotes, GetInstaller, null, App.AutoUpdateSettings.Elevated, "PRSDesktopSetup.exe"); } #endregion #region Modules public void CreatePanelAction(PanelAction action) { if (_ribbon.SelectedTabItem is null) return; var Actions = FindRibbonBar(_ribbon.SelectedTabItem, 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); } var button = new RibbonButton { Label = action.Caption, LargeIcon = action.Image.AsBitmapImage(), SizeForm = SizeForm.Large, MinWidth = 60, Tag = action }; button.Click += PaneActionClick; CurrentModules.Add(button); Actions.Items.Add(button); } } private List PanelSetupActions = new(); public void CreateSetupAction(PanelAction action) { if (_ribbon.SelectedTabItem is null) return; AddSetupAction(PanelSetupActions, action.Caption, () => { action.Execute(); }, action.Image); } private void PaneActionClick(object sender, RoutedEventArgs e) { var button = (Control)sender; var action = (PanelAction)button.Tag; action.Execute(); } private void ReloadModules(string sectionName, DataModel model) { foreach (var module in CurrentModules) { if (module.Parent is Fluent.RibbonGroupBox bar) bar.Items.Remove(module); } CurrentModules.Clear(); PanelSetupActions.Clear(); if (CurrentPanel != null) { CurrentPanel.CreateToolbarButtons(this); CreateModules(sectionName, model, new Progress>(CreateModuleButton)); } } private void CreateModules(string section, DataModel model, IProgress> progress) { if (ClientFactory.IsSupported()) { foreach (var (module, image) in CustomModuleUtils.LoadCustomModuleThumbnails(section, model)) { progress.Report(new Tuple(module.Name ?? "", image, module)); } } } private void CreateModuleButton(Tuple module) { Fluent.RibbonGroupBox? Actions = CurrentTab != null ? FindRibbonBar(CurrentTab, x => x.Header.Equals("Actions")) : null; if (Actions != null) { if (!CurrentModules.Any(x => x.GetType().Equals(typeof(RibbonSeparator)))) { var sep = new RibbonSeparator(); CurrentModules.Add(sep); Actions.Items.Add(sep); } if (!CurrentModules.Any(x => x is Fluent.Button button && module.Item1.Equals(button.Header))) { var button = new Fluent.Button { Header = module.Item1, LargeIcon = module.Item2.AsBitmapImage(), MinWidth = 60, Tag = module.Item3 }; button.Click += Module_Click; CurrentModules.Add(button); Actions.Items.Add(button); } } } private void Module_Click(object sender, RoutedEventArgs e) { if (CurrentPanel != null) { var item = (Fluent.Button)sender; var code = (CustomModule)item.Tag; if (!string.IsNullOrWhiteSpace(code.Script)) try { Selection selection; if (code.SelectedRecords && code.AllRecords) selection = RecordSelectionDialog.Execute(); else if (code.SelectedRecords) selection = Selection.Selected; else if (code.AllRecords) selection = Selection.All; else selection = Selection.None; var result = ScriptDocument.RunCustomModule(CurrentPanel.DataModel(selection), CurrentPanel.Selected(), code.Script); if (result) CurrentPanel.Refresh(); } catch (CompileException c) { MessageBox.Show(c.Message); } catch (Exception err) { MessageBox.Show(CoreUtils.FormatException(err)); } else MessageBox.Show("Unable to load " + item.Header); } } private void ManageModules() { if (CurrentPanel != null) { var section = CurrentPanel.SectionName; var dataModel = CurrentPanel.DataModel(Selection.Selected); var manager = new CustomModuleManager() { Section = section, DataModel = dataModel }; manager.ShowDialog(); ReloadModules(section, dataModel); } } private void ManageModulesClick(object sender, RoutedEventArgs e) { ManageModules(); } #endregion #region Report Buttons private void ReloadReports(string section, DataModel model) { if (CurrentTab is null) return; var ReportsBar = FindRibbonBar(CurrentTab, x => x.Header.Equals("Print")); if (ReportsBar is not null) { ReportsBar.Visibility = Security.IsAllowed() ? Visibility.Visible : Visibility.Collapsed; //ReportsBar.IsLauncherVisible = Security.IsAllowed(); ReportsBar.Items.Clear(); CreateReports(section, model, new Progress>(CreateReportButton)); } } private void CreateReports(string section, DataModel model, IProgress> progress) { var client = new Client(); var templates = client.Query( new Filter(x => x.DataModel).IsEqualTo(model.Name) .And(x => x.Section).IsEqualTo(section) .And(x => x.Visible).IsEqualTo(true), new Columns(x => x.ID, x => x.Name), new SortOrder(x => x.Name) ); foreach (var row in templates.Rows) progress.Report(new Tuple(row.Get(x => x.Name), row.Get(x => x.ID))); } public void CreateReportButton(Tuple report) { if (CurrentTab is null) return; var ReportsBar = FindRibbonBar(CurrentTab, x => x.Header.Equals("Print")); if (ReportsBar is not null) { var bFound = false; foreach (var item in ReportsBar.Items) { if (item is RibbonButton button && report.Item2.Equals(button.Tag)) bFound = true; } if (!bFound) { var button = new RibbonButton { Label = report.Item1, LargeIcon = PRSDesktop.Resources.printer.AsBitmapImage(), Tag = report.Item2, SizeForm = SizeForm.Large, MinWidth = 60 }; if (Security.IsAllowed()) { var menu = new ContextMenu(); menu.AddItem("Design Report", PRSDesktop.Resources.pencil, report.Item2, ReportButtonMenu_DesignReport_Click); button.ContextMenu = menu; } button.Click += ReportMenu_Checked; ReportsBar.Items.Add(button); } } } private void ReportButtonMenu_DesignReport_Click(Guid templateID) { if (CurrentPanel is null) return; var template = new Client().Load(new Filter(x => x.ID).IsEqualTo(templateID)).FirstOrDefault(); if (template is null) { Logger.Send(LogType.Error, "", $"No Report Template with ID '{templateID}'"); MessageBox.Show("Report does not exist!"); return; } ReportUtils.DesignReport(template, CurrentPanel.DataModel(Selection.None)); } private void ReportMenu_Checked(object sender, RoutedEventArgs e) { if (CurrentPanel is null) return; var item = (RibbonButton)sender; var id = (Guid)item.Tag; var template = new Client().Load(new Filter(x => x.ID).IsEqualTo(id)).FirstOrDefault(); if (template == null) { Logger.Send(LogType.Error, "", $"No Report Template with ID '{id}'"); MessageBox.Show("Report does not exist!"); return; } var selection = Selection.None; if (template.SelectedRecords && template.AllRecords) selection = RecordSelectionDialog.Execute(); else if (template.SelectedRecords) selection = Selection.Selected; else if (template.AllRecords) selection = Selection.All; else MessageBox.Show("Report must have either [Selected Records] or [All Records] checked to display!"); if (selection != Selection.None) ReportUtils.PreviewReport(template, CurrentPanel.DataModel(selection), false, Security.IsAllowed()); } private void ManageReports() { if (CurrentTab is null || CurrentPanel is null) return; var section = CurrentPanel.SectionName; var model = CurrentPanel.DataModel(Selection.None); if (model == null) { MessageBox.Show("No DataModel for " + CurrentTab.Header); return; } var form = new ReportManager { DataModel = model, Section = section, Populate = true }; form.ShowDialog(); ReloadReports(section, model); } private void ManageEmailTemplates() { if (CurrentTab is null || CurrentPanel is null) return; var section = CurrentPanel.SectionName; var model = CurrentPanel.DataModel(Selection.None); if (model == null) { MessageBox.Show("No DataModel for " + CurrentTab.Header); return; } var window = new EmailTemplateManagerWindow(model); window.ShowDialog(); } private void ManageReportsMenu_Click(object sender, RoutedEventArgs e) { ManageReports(); } #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), new Columns(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 DockPanel_OnIsActiveChanged(object? sender, EventArgs e) { var layout = sender as LayoutAnchorable; if (layout == null) return; var content = layout.Content as DependencyObject; var dock = content is IDockPanel panel ? panel : content?.FindVisualChildren().FirstOrDefault(); if (dock == null) return; if (layout.IsActive && layout.IsVisible) dock.Refresh(); } private void _ribbon_OnPreviewMouseDoubleClick(object sender, MouseButtonEventArgs e) { e.Handled = true; } #region Backstage Functions private void DatabaseSettings_OnClick(object sender, RoutedEventArgs e) { var config = new DataBaseConfiguration(); if (config.ShowDialog() == true) { var newsettings = new LocalConfiguration(App.Profile).Load(); if (newsettings.RestartRequired(App.DatabaseSettings)) MessageBox.Show("Please restart the application to apply these changes!"); if ((newsettings.DatabaseType == DatabaseType.Standalone) && (newsettings.ColorScheme != App.DatabaseSettings.ColorScheme)) { App.DatabaseSettings.ColorScheme = newsettings.ColorScheme; ApplyColorScheme(); } } } 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 DocumentList_OnClick(object sender, RoutedEventArgs e) { var list = new MasterList(typeof(Document)); list.ShowDialog(); } private void QAFormSetupButton_OnClick(object sender, RoutedEventArgs e) { var list = new MasterList(typeof(DigitalForm), "AppliesTo", null, true); 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(false) == ValidationResult.VALID) AfterLogin(); } private void ExitButton_OnClick(object sender, RoutedEventArgs e) { //_closingFromSystemMenu = true; Close(); } #endregion } }