MainWindow.xaml.cs 109 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Collections.ObjectModel;
  4. using System.ComponentModel;
  5. using System.Diagnostics;
  6. using System.Drawing;
  7. using System.Drawing.Drawing2D;
  8. using System.Drawing.Imaging;
  9. using System.IO;
  10. using System.Linq;
  11. using System.Net.Http;
  12. using System.Reflection;
  13. using System.Runtime.InteropServices;
  14. using System.Threading.Tasks;
  15. using System.Windows;
  16. using System.Windows.Controls;
  17. using System.Windows.Forms;
  18. using System.Windows.Input;
  19. using System.Windows.Interop;
  20. using System.Windows.Media;
  21. using System.Windows.Threading;
  22. using AvalonDock.Layout;
  23. using Comal.Classes;
  24. using Comal.Stores;
  25. using Comal.TaskScheduler.Shared;
  26. using H.Pipes;
  27. using InABox.Clients;
  28. using InABox.Configuration;
  29. using InABox.Core;
  30. using InABox.Database;
  31. using InABox.Database.SQLite;
  32. using InABox.DynamicGrid;
  33. using InABox.Mail;
  34. using InABox.Rpc;
  35. using InABox.Scripting;
  36. using InABox.Wpf;
  37. using InABox.WPF;
  38. using NAudio.Wave;
  39. using PRS.Shared;
  40. using InABox.WPF.Themes;
  41. using PRSDesktop.Configuration;
  42. using PRSDesktop.Forms;
  43. using PRSServer;
  44. using SharpAvi.Codecs;
  45. using SharpAvi.Output;
  46. using Syncfusion.Windows.Shared;
  47. using Syncfusion.Windows.Tools.Controls;
  48. using Application = System.Windows.Application;
  49. using ButtonBase = System.Windows.Controls.Primitives.ButtonBase;
  50. using Color = System.Windows.Media.Color;
  51. using ColorConverter = System.Windows.Media.ColorConverter;
  52. using Control = System.Windows.Controls.Control;
  53. using Image = System.Drawing.Image;
  54. using KeyEventArgs = System.Windows.Input.KeyEventArgs;
  55. using MessageBox = System.Windows.MessageBox;
  56. using Pen = System.Drawing.Pen;
  57. using PixelFormat = System.Drawing.Imaging.PixelFormat;
  58. using SortDirection = InABox.Core.SortDirection;
  59. using InABox.Wpf.Reports;
  60. using Comal.Classes.SecurityDescriptors;
  61. using System.Threading;
  62. using InABox.Formatters;
  63. using PRSDesktop.Forms.Issues;
  64. using Brushes = System.Windows.Media.Brushes;
  65. using System.Windows.Media.Imaging;
  66. using Button = System.Windows.Controls.Button;
  67. using Visibility = System.Windows.Visibility;
  68. using SharpVectors.Converters;
  69. using SVGImage.SVG;
  70. namespace PRSDesktop;
  71. public enum PanelType
  72. {
  73. InPlace,
  74. NewWindow
  75. }
  76. public class SimpleCommand : ICommand
  77. {
  78. public Action OnExecute { get; private set; }
  79. public bool CanExecute(object? parameter) => true;
  80. public event EventHandler? CanExecuteChanged
  81. {
  82. add => CommandManager.RequerySuggested += value;
  83. remove => CommandManager.RequerySuggested -= value;
  84. }
  85. public void Execute(object? parameter)
  86. {
  87. OnExecute?.Invoke();
  88. }
  89. public SimpleCommand(Action onExecute)
  90. {
  91. OnExecute = onExecute;
  92. }
  93. }
  94. /// <summary>
  95. /// Interaction logic for Main.xaml
  96. /// </summary>
  97. public partial class MainWindow : IPanelHostControl
  98. {
  99. //private const int WM_LBUTTONDOWN = 0x201;
  100. private static PipeServer<string>? _client;
  101. private IRpcClientTransport? _transport;
  102. private WaveIn? _audio;
  103. private bool _audioMuted;
  104. private MemoryStream? _audioStream;
  105. private readonly Dictionary<DateTime, Stream> _bitmaps = new();
  106. private DesktopConsole? _console;
  107. private Dictionary<DateTime, Tuple<Rectangle, string>> _notes = new();
  108. private DispatcherTimer? _recorder;
  109. private Process? _recordingnotes;
  110. private int _screenheight = 720;
  111. private int _screenleft;
  112. private int _screentop;
  113. private int _screenwidth = 1280;
  114. private readonly Dictionary<Guid, SecondaryWindow> SecondaryWindows = new();
  115. private CoreTable? _timesheets;
  116. private DailyActivityHistory? ActivityHistory;
  117. private readonly List<Control> CurrentModules = new();
  118. private Fluent.RibbonTabItem? CurrentTab;
  119. private Fluent.Button? CurrentButton;
  120. private readonly int FRAMES_PER_SECOND = 10;
  121. private DatabaseType DatabaseType;
  122. private readonly Dictionary<int, int> messages = new();
  123. private readonly DispatcherTimer NotificationsWatchDog;
  124. private DateTime pausestarted = DateTime.MinValue;
  125. private readonly Scheduler scheduler = new() { Interval = new TimeSpan(0, 5, 0) };
  126. // We use a Guid for the StationID rather than an IP or Mac address
  127. // because we want true single-instance restriction. Using either of
  128. // the above allows for two instances on the once machine, and thus
  129. // double-counting in the Heartbeat() function
  130. private Login station = new() { StationID = Guid.NewGuid().ToString() };
  131. private TimeSpan totalpauses = new(0);
  132. private readonly int VIDEO_HEIGHT = 1080;
  133. private readonly int VIDEO_WIDTH = 1920;
  134. private PanelHost PanelHost;
  135. public MainWindow()
  136. {
  137. PanelHost = new PanelHost(this);
  138. NotificationsWatchDog = new DispatcherTimer { IsEnabled = false };
  139. NotificationsWatchDog.Tick += Notifications_Tick;
  140. NotificationsWatchDog.Interval = new TimeSpan(0, 2, 0);
  141. ClientFactory.PushHandlers.AddHandler<Notification>(ReceiveNotification);
  142. ClientFactory.RegisterMailer(EmailType.IMAP, typeof(IMAPMailer));
  143. ClientFactory.RegisterMailer(EmailType.Exchange, typeof(ExchangeMailer));
  144. ClientFactory.OnLog += (type, userid, message, parameters) => Logger.Send(LogType.Information, ClientFactory.UserID, message, parameters);
  145. ClientFactory.OnRequestError += ClientFactory_OnRequestError;
  146. HotKeyManager.Initialize();
  147. HotKeyManager.RegisterHotKey(Key.F1, ShowHelp);
  148. //HotKeyManager.RegisterHotKey(Key.F5, ToggleRecording);
  149. //HotKeyManager.RegisterHotKey(Key.F6, ShowRecordingNotes);
  150. //HotKeyManager.RegisterHotKey(Key.F4, ToggleRecordingAudio);
  151. Logger.Send(LogType.Information, "", "Connecting to server");
  152. var settings = App.DatabaseSettings;
  153. bool dbConnected;
  154. DatabaseType = settings.DatabaseType;
  155. switch (DatabaseType)
  156. {
  157. case DatabaseType.Standalone:
  158. ClientFactory.SetClientType(typeof(LocalClient<>), Platform.Wpf, CoreUtils.GetVersion());
  159. DbFactory.ColorScheme = App.DatabaseSettings.ColorScheme;
  160. DbFactory.Logo = App.DatabaseSettings.Logo;
  161. dbConnected = true;
  162. break;
  163. case DatabaseType.Networked:
  164. if (App.DatabaseSettings.Protocol == SerializerProtocol.RPC)
  165. {
  166. _transport = new RpcClientSocketTransport(App.DatabaseSettings.URLs);
  167. _transport.OnClose += TransportConnectionLost;
  168. _transport.OnException += Transport_OnException;
  169. _transport.OnOpen += Transport_OnOpen; ;
  170. dbConnected = _transport.Connect();
  171. ClientFactory.SetClientType(typeof(RpcClient<>), Platform.Wpf, CoreUtils.GetVersion(),
  172. _transport);
  173. }
  174. else
  175. {
  176. var url = RestClient<User>.Ping(App.DatabaseSettings.URLs, out DatabaseInfo info);
  177. ClientFactory.SetClientType(typeof(RestClient<>), Platform.Wpf, CoreUtils.GetVersion(), url, true);
  178. dbConnected = true;
  179. }
  180. break;
  181. case DatabaseType.Local:
  182. //new RPC stuff (temporary disabled - for enabling when RPC is ready)
  183. var pipename = DatabaseServerProperties.GetPipeName(App.DatabaseSettings.LocalServerName, true);
  184. _transport = new RpcClientPipeTransport(pipename);
  185. _transport.OnClose += TransportConnectionLost;
  186. dbConnected = _transport.Connect();
  187. ClientFactory.SetClientType(typeof(RpcClient<>), Platform.Wpf, CoreUtils.GetVersion(), _transport );
  188. //ClientFactory.SetClientType(typeof(IPCClient<>), Platform.Wpf, CoreUtils.GetVersion(),
  189. // DatabaseServerProperties.GetPipeName(App.DatabaseSettings.LocalServerName, false));
  190. //dbConnected = true;
  191. break;
  192. default:
  193. throw new Exception($"Invalid database type {DatabaseType}");
  194. }
  195. InitializeComponent();
  196. if (!dbConnected)
  197. {
  198. switch (DoConnectionFailed())
  199. {
  200. case ConnectionFailedResult.Quit:
  201. Close();
  202. return;
  203. case ConnectionFailedResult.Restart:
  204. App.ShouldRestart = true;
  205. Close();
  206. return;
  207. case ConnectionFailedResult.Ok:
  208. // Do nothing
  209. break;
  210. }
  211. }
  212. ThemeManager.BaseColor = Colors.CornflowerBlue;
  213. Progress.DisplayImage = PRSDesktop.Resources.splash_small.AsBitmapImage();
  214. try
  215. {
  216. var dbInfo = new Client<User>().Info();
  217. ClientFactory.DatabaseID = dbInfo.DatabaseID;
  218. ThemeManager.BaseColor = (Color)ColorConverter.ConvertFromString(dbInfo.ColorScheme);
  219. if (dbInfo.Logo?.Any() == true)
  220. using (var ms = new MemoryStream(dbInfo.Logo))
  221. {
  222. Progress.DisplayImage = new Bitmap(ms).AsBitmapImage();
  223. }
  224. }
  225. catch
  226. {
  227. }
  228. //VideoRecordingStatus.Source = PRSDesktop.Resources.videorecording.AsGrayScale().AsBitmapImage();
  229. //AudioRecordingStatus.Source = PRSDesktop.Resources.audiorecording.AsGrayScale().AsBitmapImage();
  230. //SecondaryWindowStatus.Source = PRSDesktop.Resources.target.AsGrayScale().AsBitmapImage();
  231. ConsoleStatus.Source = PRSDesktop.Resources.view.AsGrayScale().AsBitmapImage();
  232. SelectTask.Source = PRSDesktop.Resources.uparrow.Invert().AsBitmapImage();
  233. Title = $"{(String.Equals(App.Profile?.ToUpper(), "DEFAULT") ? "PRS Desktop" : App.Profile)} (Release {CoreUtils.GetVersion()})";
  234. Logger.Send(LogType.Information, "", "Checking for updates");
  235. if (SupportUtils.CheckForUpdates())
  236. {
  237. Logger.Send(LogType.Information, "", "Update found, closing application.");
  238. Close();
  239. return;
  240. }
  241. Exception? startupException = null;
  242. ValidationStatus? loginStatus = null;
  243. Progress.ShowModal("Loading PRS", progress =>
  244. {
  245. DynamicGridUtils.PreviewReport = (t, m) => { ReportUtils.PreviewReport(t, m, false, Security.IsAllowed<CanDesignReports>()); };
  246. DynamicGridUtils.PrintMenu = (e, s, m, p) => { ReportUtils.PrintMenu(e, s, m, Security.IsAllowed<CanDesignReports>(), p); };
  247. ImportFactory.Register(typeof(ExcelImporter<>), "Excel File", "Excel Files (*.xls;*.xlsx;*.xlsm)|*.xls;*.xlsx;*.xlsm");
  248. ImportFactory.Register(typeof(CustomImporter<>), "Custom", "All Files (*.*)|*.*");
  249. FormUtils.Register();
  250. DigitalFormDocumentFactory.Init(
  251. new WpfDigitalFormDocumentHandler(
  252. b => Dispatcher.BeginInvoke(() =>
  253. {
  254. BackgroundUploadStatus.Visibility = b
  255. ? Visibility.Visible
  256. : Visibility.Hidden;
  257. }
  258. ),
  259. () => _transport?.IsConnected() ?? false
  260. )
  261. );
  262. DigitalFormDocumentFactory.Run();
  263. Logger.Send(LogType.Information, "", "Registering Classes");
  264. progress.Report("Registering Classes");
  265. var tasks = new List<Task>
  266. {
  267. Task.Run(() =>
  268. {
  269. CoreUtils.RegisterClasses(typeof(TaskGrid).Assembly);
  270. CoreUtils.RegisterClasses();
  271. ComalUtils.RegisterClasses();
  272. StoreUtils.RegisterClasses();
  273. PRSSharedUtils.RegisterClasses();
  274. WPFUtils.RegisterClasses();
  275. ReportUtils.RegisterClasses();
  276. ConfigurationUtils.RegisterClasses();
  277. }),
  278. Task.Run(() =>
  279. {
  280. ScriptDocument.DefaultAssemblies.AddRange(
  281. Assembly.Load("RoslynPad.Roslyn.Windows"),
  282. Assembly.Load("RoslynPad.Editor.Windows"),
  283. typeof(Control).Assembly,
  284. typeof(MessageBox).Assembly,
  285. typeof(SolidColorBrush).Assembly
  286. );
  287. ScriptDocument.Initialize();
  288. }),
  289. Task.Run(() => DatabaseUpdateScripts.RegisterScripts())
  290. };
  291. Task.WaitAll(tasks.ToArray());
  292. Logger.Send(LogType.Information, "", "Configuring Application");
  293. progress.Report("Configuring Application");
  294. RegisterModules(progress);
  295. if (DatabaseType == DatabaseType.Standalone)
  296. {
  297. progress.Report("Starting local database...");
  298. try
  299. {
  300. StartLocalDatabase(progress);
  301. }
  302. catch (Exception err)
  303. {
  304. startupException = new Exception(
  305. string.Format(
  306. "Unable to open database ({0})\n\n{1}\n\n{2}",
  307. App.DatabaseSettings.FileName,
  308. err.Message,
  309. err.StackTrace
  310. )
  311. );
  312. }
  313. }
  314. });
  315. if (startupException is null && App.DatabaseSettings.Autologin)
  316. {
  317. try
  318. {
  319. Logger.Send(LogType.Information, "", "Logging in");
  320. Dispatcher.Invoke(() =>
  321. {
  322. loginStatus = TryAutoLogin();
  323. });
  324. if(loginStatus == ValidationStatus.VALID)
  325. {
  326. // Do the AfterLogin() here so that we aren't opening and closing progress windows again and again.
  327. Progress.ShowModal("Loading PRS", progress =>
  328. {
  329. AfterLogin(progress);
  330. });
  331. }
  332. }
  333. catch(Exception e)
  334. {
  335. startupException = e;
  336. }
  337. }
  338. if (startupException != null)
  339. {
  340. MessageWindow.ShowError("Error during startup.", startupException);
  341. }
  342. // If the login status is valid, then we've already loaded everything, so we don't here.
  343. if(loginStatus != ValidationStatus.VALID)
  344. {
  345. Logger.Send(LogType.Information, "", "Logging in");
  346. if (DoLogin() == ValidationStatus.VALID)
  347. {
  348. AfterLogin(null);
  349. }
  350. else
  351. {
  352. ConfigureMainScreen(null);
  353. }
  354. }
  355. ProfileName.Content = App.Profile;
  356. URL.Content = GetDatabaseConnectionDescription();
  357. if (loginStatus == ValidationStatus.VALID && DatabaseType == DatabaseType.Standalone)
  358. {
  359. Progress.ShowModal("Starting Scheduler", progress =>
  360. {
  361. scheduler.Start();
  362. });
  363. }
  364. }
  365. #region Connection Management
  366. private string GetDatabaseConnectionDescription()
  367. {
  368. return DatabaseType switch
  369. {
  370. #if RPC
  371. DatabaseType.Networked => (ClientFactory.Parameters?.FirstOrDefault() as RpcClientSocketTransport)?.Host,
  372. #else
  373. DatabaseType.Networked => ClientFactory.Parameters?.FirstOrDefault() as string,
  374. #endif
  375. DatabaseType.Standalone => App.DatabaseSettings?.FileName,
  376. DatabaseType.Local => App.DatabaseSettings?.LocalServerName,
  377. _ => ""
  378. } ?? "";
  379. }
  380. /// <summary>
  381. /// Reconnect to the server.
  382. /// </summary>
  383. /// <returns><see langword="true"/> if connection was successful.</returns>
  384. private bool Reconnect()
  385. {
  386. if (_transport != null)
  387. {
  388. return _transport.Connect();
  389. }
  390. Logger.Send(LogType.Error, ClientFactory.UserID, "Trying to reconnect without a transport set.");
  391. return true; // Returning true so we don't get stuck in infinite loops in exceptional circumstances.
  392. }
  393. private enum ConnectionFailedResult
  394. {
  395. Quit,
  396. Restart,
  397. Ok
  398. }
  399. /// <summary>
  400. /// To be called when initial connection to the server has failed; asks the user if they want to retry,
  401. /// change the database settings, or simply quit PRS.
  402. /// </summary>
  403. /// <returns>The action to take next.</returns>
  404. /// <exception cref="Exception"></exception>
  405. private ConnectionFailedResult DoConnectionFailed()
  406. {
  407. bool connected = false;
  408. while (!connected)
  409. {
  410. var connectionFailedWindow = new ConnectionFailed();
  411. connectionFailedWindow.ShowDialog();
  412. var reconnect = false;
  413. switch (connectionFailedWindow.Result)
  414. {
  415. case ConnectionFailedWindowResult.OpenDatabaseConfiguration:
  416. var result = ShowDatabaseConfiguration();
  417. switch (result)
  418. {
  419. case DatabaseConfigurationResult.RestartRequired:
  420. var shouldRestart = MessageBox.Show(
  421. "A restart is required to apply these changes. Do you wish to restart now?",
  422. "Restart?",
  423. MessageBoxButton.YesNo);
  424. if (shouldRestart == MessageBoxResult.Yes)
  425. {
  426. return ConnectionFailedResult.Restart;
  427. }
  428. else
  429. {
  430. reconnect = true;
  431. }
  432. break;
  433. case DatabaseConfigurationResult.RestartNotRequired:
  434. reconnect = true;
  435. break;
  436. case DatabaseConfigurationResult.Cancel:
  437. reconnect = true;
  438. break;
  439. default:
  440. throw new Exception($"Invalid enum result {result}");
  441. }
  442. break;
  443. case ConnectionFailedWindowResult.RetryConnection:
  444. reconnect = true;
  445. break;
  446. case ConnectionFailedWindowResult.Quit:
  447. return ConnectionFailedResult.Quit;
  448. default:
  449. throw new Exception($"Invalid enum result {connectionFailedWindow.Result}");
  450. }
  451. if (!reconnect)
  452. {
  453. return ConnectionFailedResult.Quit;
  454. }
  455. connected = Reconnect();
  456. }
  457. return ConnectionFailedResult.Ok;
  458. }
  459. private void Transport_OnOpen(IRpcTransport transport, RpcTransportOpenArgs e)
  460. {
  461. Logger.Send(LogType.Information, ClientFactory.UserID, "Connection opened");
  462. }
  463. private void Transport_OnException(IRpcTransport transport, RpcTransportExceptionArgs e)
  464. {
  465. Logger.Send(LogType.Error, ClientFactory.UserID, $"Error in connection: {CoreUtils.FormatException(e.Exception)}");
  466. }
  467. private void TransportConnectionLost(IRpcTransport transport, RpcTransportCloseArgs e)
  468. {
  469. Logger.Send(LogType.Information, ClientFactory.UserID, "Connection lost");
  470. if (transport is IRpcClientTransport client)
  471. {
  472. Dispatcher.Invoke(() =>
  473. {
  474. var reconnection = new ReconnectionWindow();
  475. var cancellationTokenSource = new CancellationTokenSource();
  476. reconnection.OnCancelled = () => cancellationTokenSource.Cancel();
  477. var ct = cancellationTokenSource.Token;
  478. var work = () =>
  479. {
  480. try
  481. {
  482. DateTime lost = DateTime.Now;
  483. while (!client.IsConnected() && !ct.IsCancellationRequested)
  484. {
  485. try
  486. {
  487. Logger.Send(LogType.Error, ClientFactory.UserID, $"Reconnecting - ({DateTime.Now - lost:hh\\:mm})");
  488. if (client.Connect(ct))
  489. {
  490. break;
  491. }
  492. }
  493. catch (System.Exception e1)
  494. {
  495. Logger.Send(LogType.Error, ClientFactory.UserID, $"Reconnect Failed: {e1.Message}");
  496. // TODO: Remove this suppression
  497. if (e1.Message.StartsWith("The socket is connected, you needn't connect again!"))
  498. {
  499. break;
  500. }
  501. }
  502. }
  503. if (client.IsConnected())
  504. {
  505. Logger.Send(LogType.Information, ClientFactory.UserID, "Reconnected");
  506. ClientFactory.Validate(ClientFactory.SessionID);
  507. Logger.Send(LogType.Information, ClientFactory.UserID, "Validated");
  508. return true;
  509. }
  510. }
  511. catch (Exception e)
  512. {
  513. Logger.Send(LogType.Error, ClientFactory.UserID, $"Reconnect Failed: {e.Message}");
  514. }
  515. return false;
  516. };
  517. var task = Task.Run(() =>
  518. {
  519. var result = work();
  520. Dispatcher.Invoke(() =>
  521. {
  522. reconnection.Close();
  523. });
  524. return result;
  525. }, ct);
  526. reconnection.ShowDialog();
  527. if (!task.Result)
  528. {
  529. Close();
  530. }
  531. });
  532. }
  533. }
  534. #endregion
  535. private bool _loggingOut = false;
  536. private void ClientFactory_OnRequestError(RequestException e)
  537. {
  538. if (e.Status == StatusCode.Unauthenticated)
  539. {
  540. switch (e.Method)
  541. {
  542. case RequestMethod.Query:
  543. case RequestMethod.Save:
  544. case RequestMethod.Delete:
  545. case RequestMethod.MultiQuery:
  546. case RequestMethod.MultiSave:
  547. case RequestMethod.MultiDelete:
  548. if (!_loggingOut)
  549. {
  550. Dispatcher.InvokeAsync(() =>
  551. {
  552. _loggingOut = true;
  553. try
  554. {
  555. Logout(null, true);
  556. }
  557. finally
  558. {
  559. _loggingOut = false;
  560. }
  561. });
  562. }
  563. break;
  564. default:
  565. break;
  566. }
  567. }
  568. }
  569. private void ApplyColorScheme()
  570. {
  571. Color baseColor;
  572. try
  573. {
  574. baseColor = (Color)ColorConverter.ConvertFromString(App.DatabaseSettings.ColorScheme);
  575. }
  576. catch
  577. {
  578. baseColor = Colors.CornflowerBlue;
  579. }
  580. ThemeManager.BaseColor = baseColor;
  581. DynamicGridUtils.SelectionBackground = ThemeManager.SelectionBackgroundBrush;
  582. DynamicGridUtils.SelectionForeground = ThemeManager.SelectionForegroundBrush;
  583. DynamicGridUtils.FilterBackground = ThemeManager.FilterBackgroundBrush;
  584. DynamicGridUtils.FilterForeground = ThemeManager.FilterForegroundBrush;
  585. UpdateRibbonColors();
  586. PanelHost.Refresh();
  587. }
  588. #region Configuration
  589. private bool CanViewMaps => Security.CanView<Equipment>()
  590. || Security.CanView<Job>()
  591. || Security.CanView<TimeSheet>()
  592. || Security.CanView<GPSTracker>();
  593. private void ConfigureMainScreen(IProgress<string>? progress)
  594. {
  595. var bMaps = CanViewMaps;
  596. ProgressSection ConfigureTab(string progress, Func<Fluent.RibbonTabItem?> createTab)
  597. {
  598. return new ProgressSection(progress, () =>
  599. {
  600. var tab = createTab();
  601. if (tab is not null)
  602. {
  603. _ribbon.Tabs.Add(tab);
  604. }
  605. });
  606. }
  607. var sections = new[]
  608. {
  609. new ProgressSection("Initial Setup", () =>
  610. {
  611. _ribbon.Tabs.Clear();
  612. }),
  613. new ProgressSection("Configuring Main Screen", SetupMainScreen),
  614. ConfigureTab("Configuring Projects", SetupProjectsTab),
  615. ConfigureTab("Configuring Manufacturing", SetupManufacturingTab),
  616. ConfigureTab("Configuring Logistics", SetupLogisticsTab),
  617. ConfigureTab("Configuring Products", SetupProductsTab),
  618. ConfigureTab("Configuring Human Resources", SetupHumanResourcesTab),
  619. ConfigureTab("Configuring Accounts Receivable", SetupAccountsReceivableTab),
  620. ConfigureTab("Configuring Accounts Payable", SetupAccountsPayableTab),
  621. ConfigureTab("Configuring Equipment", SetupEquipmentTab),
  622. ConfigureTab("Configuring DigitalForms", SetupDigitalFormsTab),
  623. ConfigureTab("Configuring Dashboards", SetupDashboardsTab),
  624. new ProgressSection("Configuring System Modules", SetupSystemModules)
  625. };
  626. if(progress is not null)
  627. {
  628. Dispatcher.Invoke(SetupScreen);
  629. foreach(var section in sections)
  630. {
  631. progress.Report(section.Message);
  632. Dispatcher.Invoke(section.Action);
  633. }
  634. }
  635. else
  636. {
  637. SetupScreen();
  638. Progress.ShowModal(sections);
  639. }
  640. }
  641. private void SetupScreen()
  642. {
  643. var button = _ribbon.FindVisualChildren<Fluent.DropDownButton>().FirstOrDefault();
  644. if (button != null)
  645. button.Visibility = Visibility.Collapsed;
  646. if (ClientFactory.UserGuid == Guid.Empty)
  647. _ribbonRow.Height = new GridLength(30, GridUnitType.Pixel);
  648. else
  649. _ribbonRow.Height = new GridLength(1, GridUnitType.Auto);
  650. }
  651. private void SetupMainScreen()
  652. {
  653. //DockManager.SidePanelSize = OutstandingDailyReports(false) ? 0.00F : 30.00F;
  654. // Notifications Area
  655. SetFrameworkItemVisibility(SendNotification, Security.CanView<Notification>());
  656. SetFrameworkItemVisibility(Notifications, Security.CanView<Notification>());
  657. SetFrameworkItemVisibility(TaskTracking, Security.IsAllowed<CanTrackTasksInDailyReport>());
  658. UserID.Content = ClientFactory.UserID;
  659. if (ClientFactory.PasswordExpiration != DateTime.MinValue)
  660. {
  661. var timeUntilExpiration = ClientFactory.PasswordExpiration - DateTime.Now;
  662. if (timeUntilExpiration.Days < 14)
  663. {
  664. PasswordExpiryNotice.Content = $"Password will expire in {timeUntilExpiration.Days} days!";
  665. PasswordExpiryNotice.Visibility = Visibility.Visible;
  666. }
  667. else
  668. {
  669. PasswordExpiryNotice.Visibility = Visibility.Collapsed;
  670. }
  671. }
  672. }
  673. private void SetupSystemModules()
  674. {
  675. SetFrameworkItemVisibility(CompanyInformation, Security.CanView<CompanyInformation>());
  676. SetVisibleIfAny(BackstageSeparator0, CompanyInformation);
  677. SetFrameworkItemVisibility(SecurityDefaultsButton, Security.IsAllowed<CanCustomiseSecurityDefaults>());
  678. SetVisibleIfAny(BackstageSeparator1, SecurityDefaultsButton);
  679. BackstageSeparator1a.Visibility = Visibility.Visible;
  680. SystemLogsButton.Visibility = Visibility.Visible;
  681. SetFrameworkItemVisibility(DocumentTypeList, Security.IsAllowed<CanViewDocumentTypes>());
  682. SetFrameworkItemVisibility(EventList, Security.IsAllowed<CanManageEvents>());
  683. SetVisibleIfAny(BackstageSeparator2, DocumentTypeList, EventList);
  684. //SetModuleVisibility<>(VideoRecordingButton, Security.IsAllowed<CanRecordScreen>());
  685. LogoutButton.Visibility = ClientFactory.UserGuid == Guid.Empty ? Visibility.Collapsed : Visibility.Visible;
  686. LoginButton.Visibility = ClientFactory.UserGuid != Guid.Empty ? Visibility.Collapsed : Visibility.Visible;
  687. EditDetailsButton.Visibility = ClientFactory.UserGuid == Guid.Empty ? Visibility.Collapsed : Visibility.Visible;
  688. SetupDock<CanViewContactsDock>(ContactDock, Contacts);
  689. SetupDock<CanViewJobDock>(JobDock, Jobs);
  690. SetupDock<CanViewConsignmentDock>(ConsignmentDock, Consignments);
  691. SetupDock<CanViewDeliveryDock>(DeliveryDock, Deliveries);
  692. SetupDock<CanViewProductDock>(ProductLookupDock, ProductLookup);
  693. SetupDock<CanViewDigitalFormsDock>(DigitalFormsDock, DigitalForms);
  694. SetupDock<CanViewProblemsDock>(ProblemsDock, Problems);
  695. SetupDock<CanViewRequisitionsDock>(RequisitionsDock, Requisitions);
  696. _ribbon.InvalidateArrange();
  697. }
  698. private Fluent.RibbonTabItem? SetupDashboardsTab() =>
  699. CreateTab<ViewDesktopDashboardsTab>("Dashboards", x => x
  700. .NewGroup(x => x
  701. .Add<DatabaseActivityDashboard>("Database Activity", SvgImages.kpi,
  702. Security.IsAllowed<CanViewDatabaseActivity>()
  703. && Security.IsAllowed<ViewDesktopDatabaseActivityDashboard>())
  704. .Add<UserActivity>("User Activity", SvgImages.kpi,
  705. Security.IsAllowed<CanViewUserActivity>()
  706. && Security.IsAllowed<ViewDesktopUserActivityDashboard>())
  707. .Add<WidgetDashboard>("Quick Status", SvgImages.kpi,
  708. Security.IsAllowed<CanViewQuickStatus>() && Security.IsAllowed<ViewDesktopQuickStatusDashboard>())));
  709. private Fluent.RibbonTabItem? SetupDigitalFormsTab() =>
  710. CreateTab<ViewDesktopDigitalFormsTab>("Digital Forms", x => x
  711. .NewGroup(x => x
  712. .Add<DigitalFormsLibrary>("Forms Library", SvgImages.formslibrary,
  713. Security.IsAllowed<CanAdministerDigitalFormsLibrary>())
  714. .Add<CompletedFormsPanel>("Forms Dashboard", SvgImages.formsinstance,
  715. Security.IsAllowed<CanViewDigitalFormsDashbaord>()
  716. && Security.IsAllowed<ViewDesktopDigitalFormsDashboard>())));
  717. private Fluent.RibbonTabItem? SetupEquipmentTab() =>
  718. CreateTab<ViewDesktopEquipmentTab>("Equipment", x => x
  719. .NewGroup(x => x
  720. .Add<EquipmentPanel>("Equipment List", PRSDesktop.Resources.specifications,
  721. Security.CanView<Equipment>()
  722. && Security.IsAllowed<ViewDesktopEquipmentListScreen>())
  723. .Add<EquipmentMaintenancePanel>("Planned Maintenance", PRSDesktop.Resources.service,
  724. Security.CanView<Equipment>()
  725. && Security.IsAllowed<ViewDesktopEquipmentMaintenanceScreen>())
  726. .Add<EquipmentPlannerPanel>("Equipment Planner", PRSDesktop.Resources.calendar,
  727. Security.CanView<Equipment>()
  728. && Security.CanView<Assignment>()
  729. && Security.IsAllowed<ViewDesktopEquipmentPlannerScreen>())
  730. .Add<GPSTrackers>("GPS Trackers", PRSDesktop.Resources.milestone,
  731. Security.CanView<GPSTracker>() && Security.IsAllowed<ViewDesktopGPSTrackersScreen>())));
  732. private Fluent.RibbonTabItem? SetupAccountsReceivableTab() =>
  733. CreateTab<ViewDesktopAccountsTab>("Accounts Receivable", x => x
  734. .NewGroup(x => x
  735. .Add<CustomerPanel>("Customers", PRSDesktop.Resources.customer,
  736. Security.CanView<Customer>()
  737. && Security.IsAllowed<ViewDesktopCustomersScreen>())
  738. .Add<InvoicePanel>("Invoices", PRSDesktop.Resources.invoice,
  739. Security.CanView<Invoice>()
  740. && Security.IsAllowed<ViewDesktopInvoicesScreen>())
  741. .Add<CustomerReceipts>("Receipts", PRSDesktop.Resources.receipt,
  742. Security.CanView<Receipt>()
  743. && Security.IsAllowed<ViewDesktopReceiptsScreen>())));
  744. private Fluent.RibbonTabItem? SetupAccountsPayableTab() =>
  745. CreateTab<ViewDesktopAccountsTab>("Accounts Payable", x => x
  746. .NewGroup(x => x
  747. .Add<SupplierPanel>("Suppliers", PRSDesktop.Resources.supplier,
  748. Security.CanView<Supplier>()
  749. && Security.IsAllowed<ViewDesktopSuppliersScreen>())
  750. .Add<DataEntryPanel>("Data Entry", PRSDesktop.Resources.pencil,
  751. Security.IsAllowed<CanViewDataEntryPanel>())
  752. .Add<SupplierPurchaseOrderPanel>("Purchase Orders", PRSDesktop.Resources.purchase,
  753. Security.CanView<PurchaseOrder>()
  754. && Security.IsAllowed<ViewDesktopPurchaseOrdersScreen>())
  755. .Add<SupplierBillPanel>("Bills", PRSDesktop.Resources.bill,
  756. Security.CanView<Bill>()
  757. && Security.IsAllowed<ViewDesktopBillsScreen>())
  758. .Add<SupplierPayments>("Payments", PRSDesktop.Resources.payment,
  759. Security.CanView<Payment>()
  760. && Security.IsAllowed<ViewDesktopPaymentsScreen>())));
  761. private Fluent.RibbonTabItem? SetupHumanResourcesTab() =>
  762. CreateTab<ViewDesktopHumanResourcesTab>("Human Resources", x => x
  763. .NewGroup(x => x
  764. .Add<CalendarPanel>("Calendar", PRSDesktop.Resources.assignments,
  765. Security.CanView<Assignment>() && Security.IsAllowed<ViewDesktopCalendarScreen>())
  766. .Add<EmployeeResourcePlannerPanel>("Employee Planner", PRSDesktop.Resources.calendar,
  767. Security.CanView<Assignment>() && Security.IsAllowed<ViewDesktopEmployeePlannerScreen>())
  768. .Add<TimesheetPanel>("Staff Timesheets", PRSDesktop.Resources.clock,
  769. Security.CanView<TimeSheet>() && Security.IsAllowed<ViewDesktopStaffTimeSheetsScreen>())
  770. .Add<LeaveRequestPanel>("Leave Requests", SvgImages.beach,
  771. Security.CanView<LeaveRequest>() && Security.IsAllowed<ViewDesktopLeaveRequestsScreen>())
  772. .Add<MeetingPanel>("Meetings", PRSDesktop.Resources.employees,
  773. Security.IsAllowed<ViewDesktopMeetingsScreen>()))
  774. .NewGroup(x => x
  775. .Add<UserPanel>("User Accounts", PRSDesktop.Resources.user,
  776. Security.CanView<User>() && Security.IsAllowed<ViewDesktopUserAccountsScreen>())
  777. .Add<EmployeePanel>("Employee List", PRSDesktop.Resources.employee,
  778. Security.CanView<Employee>() && Security.IsAllowed<ViewDesktopEmployeeListScreen>())
  779. .Add<OrgChartPanel>("Org Chart", PRSDesktop.Resources.orgchart,
  780. Security.IsAllowed<ViewDesktopOrgChartScreen>())));
  781. private Fluent.RibbonTabItem? SetupProductsTab() =>
  782. CreateTab<ViewDesktopProductManagementTab>("Products", x => x
  783. .NewGroup(x => x
  784. .Add<ProductsPanel>("Product List", PRSDesktop.Resources.product,
  785. Security.CanView<Product>() && Security.IsAllowed<ViewDesktopProductListScreen>())
  786. .Add<StockLocationPanel>("Stock Locations", PRSDesktop.Resources.parcel,
  787. Security.CanView<StockLocation>() && Security.IsAllowed<ViewDesktopStockLocationsScreen>())
  788. .Add<StockMovementPanel>("Stock Movements", PRSDesktop.Resources.forklift,
  789. Security.CanView<StockMovement>() && Security.IsAllowed<ViewDesktopStockMovementsScreen>())
  790. .Add<StockForecastPanel>("Stock Forecast", SvgImages.kpi,
  791. Security.CanView<Product>()
  792. && Security.CanView<JobMaterial>()
  793. && Security.IsAllowed<ViewDesktopStockForecastScreen>())
  794. .Add<ReservationManagementPanel>("Reservation Management", PRSDesktop.Resources.requisition,
  795. Security.IsAllowed<ViewDesktopReservationManagementScreen>())));
  796. private Fluent.RibbonTabItem? SetupLogisticsTab() =>
  797. CreateTab<ViewDesktopLogisticsTab>("Logistics", x => x
  798. .NewGroup(x => x
  799. .Add<ReadyToGoPanel>("Ready To Go", SvgImages.truck,
  800. Security.IsAllowed<CanViewLogisticsReadyToGo>()
  801. && Security.IsAllowed<ViewDesktopReadyToGoScreen>())
  802. .Add<DispatchPanel>("Rack List", PRSDesktop.Resources.barcode,
  803. Security.CanView<Shipment>()
  804. && Security.CanView<DeliveryItem>()
  805. && Security.IsAllowed<ViewDesktopRackListScreen>())
  806. .Add<RequisitionPanel>("Picking Lists", SvgImages.box,
  807. Security.CanView<Requisition>() && Security.IsAllowed<ViewDesktopSiteRequisitionsScreen>())
  808. .Add<DeliveryPanel>("Deliveries", SvgImages.truck,
  809. Security.IsAllowed<CanViewDeliveriesModule>() && Security.IsAllowed<ViewDesktopDeliveriesScren>())
  810. .Add<DeliveredOnSitePanel>("Delivered On Site", PRSDesktop.Resources.lifter,
  811. Security.IsAllowed<CanViewDeliveredOnSite>()
  812. && Security.IsAllowed<ViewDesktopDeliveredOnSiteScreen>()))
  813. .NewGroup(x => x
  814. .Add<ConsignmentsPanel>("Incoming Consignments", PRSDesktop.Resources.consignment,
  815. Security.CanView<Consignment>() && Security.IsAllowed<ViewDesktopIncomingConsignmentsScreen>())));
  816. private Fluent.RibbonTabItem? SetupManufacturingTab() =>
  817. CreateTab<ViewDesktopManufacturingTab>("Manufacturing", x => x
  818. .NewGroup(x => x
  819. .Add<StagingPanel>("Design Management", PRSDesktop.Resources.design,
  820. Security.CanView<Job>() && Security.IsAllowed<ViewDesktopDesignManagementScreen>()))
  821. .NewGroup(x => x
  822. .Add<ManufacturingPanel>("Manufacturing Status", PRSDesktop.Resources.factory,
  823. Security.IsAllowed<CanViewFactoryStatus>()
  824. && Security.IsAllowed<ViewDesktopManufacturingStatusScreen>())
  825. .Add<ManufacturingAllocationPanel>("Factory Allocation", PRSDesktop.Resources.assignments,
  826. Security.IsAllowed<CanViewFactoryAllocation>()
  827. && Security.IsAllowed<ViewDesktopFactoryAllocationScreen>())
  828. .Add<FactoryPanel>("Factory Floor", PRSDesktop.Resources.wrench,
  829. Security.IsAllowed<CanViewFactoryFloor>()
  830. && Security.IsAllowed<ViewDesktopFactoryFloorScreen>()))
  831. .NewGroup(x => x
  832. .Add<FactoryProductivityDashboard>("Factory KPIs", SvgImages.kpi,
  833. Security.IsAllowed<CanViewFactoryKPIs>()
  834. && Security.IsAllowed<ViewDesktopFactoryKPIsDashboard>())
  835. .Add<ManufacturingTemplateAnalysis>("Template Analysis", SvgImages.kpi,
  836. Security.IsAllowed<CanViewTemplateAnalysis>()
  837. && Security.IsAllowed<ViewDesktopTemplateAnalysisDashboard>())
  838. .Add<FactoryFloorAnalysis>("Factory Analysis", SvgImages.kpi,
  839. Security.IsAllowed<CanViewFactoryAnalysis>()
  840. && Security.IsAllowed<ViewDesktopFactoryAnalysisDashboard>())));
  841. private Fluent.RibbonTabItem? SetupProjectsTab() =>
  842. CreateTab<ViewDesktopProjectsTab>("Projects", x => x
  843. .NewGroup(x => x
  844. .Add<QuotePanel>("Quotes", PRSDesktop.Resources.quotation,
  845. Security.CanView<Quote>() && Security.IsAllowed<ViewDesktopQuotesScreen>())
  846. .Add<ProjectsPanel>("Projects", PRSDesktop.Resources.project,
  847. Security.CanView<Job>() && Security.IsAllowed<ViewDesktopProjectsScreen>())
  848. .Add<JobResourcePlannerPanel>("Project Planner", PRSDesktop.Resources.calendar,
  849. Security.CanView<Job>() && Security.IsAllowed<ViewDesktopProjectPlannerScreen>()))
  850. .NewGroup(x => x
  851. .Add<KitPanel>("Product Kits", PRSDesktop.Resources.kit,
  852. Security.CanView<Kit>() && Security.IsAllowed<ViewDesktopProductKitsScreen>())
  853. .Add<CostSheetPanel>("Cost Sheets", PRSDesktop.Resources.costsheet,
  854. Security.CanView<CostSheet>() && Security.IsAllowed<ViewDesktopCostSheetsScreen>()),
  855. groupName: "Setup"));
  856. private void SetupDock<TSecurityDescriptor>(LayoutAnchorable layout, IDockPanel dock)
  857. where TSecurityDescriptor : ISecurityDescriptor, new()
  858. {
  859. if (Security.IsAllowed<TSecurityDescriptor>())
  860. {
  861. if (!DockGroup.Children.Any(x => x == layout))
  862. {
  863. DockGroup.Children.Add(layout);
  864. }
  865. if (layout.IsVisible && (ClientFactory.UserGuid != Guid.Empty))
  866. dock.Setup();
  867. }
  868. else
  869. {
  870. DockGroup.RemoveChild(layout);
  871. }
  872. }
  873. private class HeaderTab(string header, MainWindow window)
  874. {
  875. public string Header { get; set; } = header;
  876. public List<Tuple<string, List<HeaderGroup>>> Groups { get; set; } = new();
  877. public HeaderTab NewGroup(Action<HeaderGroup> modify, string? groupName = null)
  878. {
  879. var currentGroup = (groupName is not null ? Groups.FirstOrDefault(x => x.Item1 == groupName) : Groups.LastOrDefault())?.Item2;
  880. if(currentGroup is null)
  881. {
  882. currentGroup = new List<HeaderGroup>();
  883. Groups.Add(new(groupName ?? "", currentGroup));
  884. }
  885. var group = new HeaderGroup(window);
  886. currentGroup.Add(group);
  887. modify(group);
  888. return this;
  889. }
  890. public Fluent.RibbonTabItem? Create()
  891. {
  892. var item = new Fluent.RibbonTabItem
  893. {
  894. Header = Header
  895. };
  896. var hasNonDefault = false;
  897. foreach(var (name, groups) in Groups)
  898. {
  899. var groupBox = new Fluent.RibbonGroupBox
  900. {
  901. Header = name
  902. };
  903. var needsSeparator = false;
  904. foreach(var group in groups)
  905. {
  906. var addedSeparator = false;
  907. foreach(var button in group.Actions)
  908. {
  909. if (button.Visibility != Visibility.Visible) continue;
  910. if (!addedSeparator && needsSeparator)
  911. {
  912. groupBox.Items.Add(new RibbonSeparator());
  913. needsSeparator = false;
  914. addedSeparator = true;
  915. }
  916. if (!group.IsDefaultGroup)
  917. {
  918. hasNonDefault = true;
  919. }
  920. groupBox.Items.Add(button);
  921. needsSeparator = true;
  922. }
  923. }
  924. if (groupBox.Items.Count > 0)
  925. {
  926. item.Groups.Add(groupBox);
  927. }
  928. }
  929. return hasNonDefault ? item : null;
  930. }
  931. }
  932. private class HeaderGroup(MainWindow window)
  933. {
  934. public List<Fluent.Button> Actions { get; set; } = new();
  935. public bool IsDefaultGroup { get; set; } = false;
  936. private bool _locked = false;
  937. private Fluent.Button CreateButton(string name, ImageSource image)
  938. {
  939. if (_locked) throw new Exception("Header Group is locked!");
  940. var button = new Fluent.Button
  941. {
  942. Header = name,
  943. LargeIcon = image,
  944. MinWidth = 60
  945. };
  946. Actions.Add(button);
  947. return button;
  948. }
  949. private Fluent.Button CreateButton(string name, Bitmap image)
  950. {
  951. return CreateButton(name, image.AsBitmapImage());
  952. }
  953. public HeaderGroup Add(string name, ImageSource image, Action onClick)
  954. {
  955. var button = CreateButton(name, image);
  956. button.Command = new SimpleCommand(() => onClick());
  957. return this;
  958. }
  959. public HeaderGroup Add(string name, Bitmap image, Action onClick)
  960. {
  961. var button = CreateButton(name, image);
  962. button.Command = new SimpleCommand(() => onClick());
  963. return this;
  964. }
  965. public HeaderGroup Add<T>(string name, ImageSource image, bool visible = true)
  966. where T : class, IBasePanel, new()
  967. {
  968. var button = CreateButton(name, image);
  969. button.Command = new SimpleCommand(() => window.LoadWindow<T>(button));
  970. var menu = new ContextMenu();
  971. menu.AddItem("Open in New Window", PRSDesktop.Resources.target, () => window.LoadSecondaryWindow<T>(button));
  972. button.ContextMenu = menu;
  973. window.SetFrameworkItemVisibility(button, visible);
  974. return this;
  975. }
  976. public HeaderGroup Add<T>(string name, Bitmap image, bool visible = true)
  977. where T : class, IBasePanel, new()
  978. {
  979. return Add<T>(name, image.AsBitmapImage(), visible: visible);
  980. }
  981. public HeaderGroup Lock()
  982. {
  983. _locked = true;
  984. return this;
  985. }
  986. }
  987. private Fluent.RibbonTabItem? CreateTab(string header, Action<HeaderTab> modify)
  988. {
  989. var tab = new HeaderTab(header, this)
  990. .NewGroup(x => x
  991. .Add("Refresh", PRSDesktop.Resources.refresh, PanelHost.Refresh)
  992. .Lock(),
  993. groupName: "Actions")
  994. .NewGroup(x => x
  995. .Add<UtilityDashboard>("Dashboards", SvgImages.kpi, Security.IsAllowed<CanViewUserDefinedDashboards>())
  996. .Add<NotificationPanel>("Notification Centre", PRSDesktop.Resources.email, Security.CanView<Notification>())
  997. .Add<TaskPanel>("Task List", SvgImages.kanban, Security.CanView<Kanban>())
  998. .Add<AttendancePanel>("In/Out Board", PRSDesktop.Resources.attendance, Security.IsAllowed<CanViewInOutBoard>())
  999. .Add<LiveMapsPanel>("Live Maps", PRSDesktop.Resources.map, CanViewMaps)
  1000. .Add<DailyReport>("Daily Report", PRSDesktop.Resources.report, Security.IsAllowed<CanViewDailyReports>())
  1001. .Lock());
  1002. foreach(var (_, groupList) in tab.Groups)
  1003. {
  1004. foreach(var group in groupList)
  1005. {
  1006. group.IsDefaultGroup = true;
  1007. }
  1008. }
  1009. modify(tab);
  1010. var result = tab.Create();
  1011. if(result is null)
  1012. {
  1013. // This means no other items were added; in this case don't create the tab.
  1014. return null;
  1015. }
  1016. var box = new Fluent.RibbonGroupBox
  1017. {
  1018. Header = "Print",
  1019. Visibility = Visibility.Collapsed
  1020. };
  1021. result.Groups.Add(box);
  1022. return result;
  1023. }
  1024. private Fluent.RibbonTabItem? CreateTab<TSecurity>(string header, Action<HeaderTab> modify)
  1025. where TSecurity : ISecurityDescriptor, new()
  1026. {
  1027. if (!Security.IsAllowed<TSecurity>()) return null;
  1028. return CreateTab(header, modify);
  1029. }
  1030. #region Visibility
  1031. private void SetFrameworkItemVisibility(FrameworkElement button, bool visible)
  1032. {
  1033. var vResult = true;
  1034. var eResult = ClientFactory.UserGuid != Guid.Empty && visible;
  1035. button.Visibility = vResult && eResult ? Visibility.Visible : Visibility.Collapsed;
  1036. if (button is Fluent.Button rb)
  1037. {
  1038. CustomModules.Register(rb.Header?.ToString() ?? "");
  1039. rb.IsEnabled = !OutstandingDailyReports(false);
  1040. }
  1041. }
  1042. private void SetModuleVisibility<T>(Fluent.Button button, bool visible) where T : class, IBasePanel, new()
  1043. {
  1044. SetFrameworkItemVisibility(button,visible);
  1045. button.MinWidth = 60;
  1046. if (button.Command == null)
  1047. {
  1048. button.Command = new SimpleCommand(() => LoadWindow<T>(button));
  1049. var menu = new ContextMenu();
  1050. menu.Items.Add(new MenuItem()
  1051. {
  1052. Header = "Open in New Window",
  1053. Icon = new System.Windows.Controls.Image() { Source = PRSDesktop.Resources.target.AsBitmapImage() },
  1054. Command = new SimpleCommand(() => LoadSecondaryWindow<T>(button))
  1055. });
  1056. button.ContextMenu = menu;
  1057. }
  1058. }
  1059. private static void SetVisibleIfEither(FrameworkElement separator, FrameworkElement[] left, FrameworkElement[] right)
  1060. {
  1061. var bLeft = false;
  1062. foreach (var button in left)
  1063. bLeft = bLeft || button.Visibility == Visibility.Visible;
  1064. var bRight = false;
  1065. foreach (var button in right)
  1066. bRight = bRight || button.Visibility == Visibility.Visible;
  1067. separator.Visibility = bLeft && bRight ? Visibility.Visible : Visibility.Collapsed;
  1068. }
  1069. private static void SetVisibleIfAny(FrameworkElement separator, params FrameworkElement[] buttons)
  1070. {
  1071. var bVisible = false;
  1072. foreach (var button in buttons)
  1073. bVisible = bVisible || button.Visibility == Visibility.Visible;
  1074. separator.Visibility = bVisible ? Visibility.Visible : Visibility.Collapsed;
  1075. }
  1076. private static void SetTabVisibleIfAny(Fluent.RibbonTabItem tab, params FrameworkElement[] buttons)
  1077. {
  1078. var bVisible = false;
  1079. foreach (var button in buttons)
  1080. bVisible = bVisible || button.Visibility == Visibility.Visible;
  1081. tab.Visibility = bVisible ? Visibility.Visible : Visibility.Collapsed;
  1082. }
  1083. #endregion
  1084. private Fluent.Button? FindModuleButton(string name)
  1085. {
  1086. foreach (var tab in _ribbon.Tabs)
  1087. {
  1088. foreach (var bar in tab.Groups)
  1089. {
  1090. var item = bar.Items.OfType<Fluent.Button>().FirstOrDefault(x => Equals(x.Header, name));
  1091. if (item is not null) return item;
  1092. }
  1093. }
  1094. return null;
  1095. }
  1096. private void LoadApplicationState()
  1097. {
  1098. if (ClientFactory.UserGuid != Guid.Empty)
  1099. {
  1100. _ribbon.IsCollapsed = false;
  1101. if (OutstandingDailyReports(false))
  1102. {
  1103. MessageWindow.ShowMessage("There are outstanding Daily Reports that must be filled out before continuing!"
  1104. + "\n\nAccess to PRS is restricted until this is corrected.",
  1105. "Outstanding Reports");
  1106. if(FindModuleButton("Daily Report") is Fluent.Button button)
  1107. {
  1108. var dailyReportPanel = LoadWindow<DailyReport>(button);
  1109. if(dailyReportPanel is not null)
  1110. {
  1111. dailyReportPanel.OnTimeSheetConfirmed += e =>
  1112. {
  1113. if (!OutstandingDailyReports(true))
  1114. {
  1115. ConfigureMainScreen(null);
  1116. LoadApplicationState();
  1117. }
  1118. };
  1119. }
  1120. }
  1121. return;
  1122. }
  1123. using (new WaitCursor())
  1124. {
  1125. _ribbon.IsCollapsed = false;
  1126. LoadInitialWindow();
  1127. }
  1128. }
  1129. }
  1130. private void LoadInitialWindow()
  1131. {
  1132. var app = new LocalConfiguration<AppSettings>().Load();
  1133. var module = app.Settings.ContainsKey("CurrentPanel")
  1134. ? !string.IsNullOrWhiteSpace(app.Settings["CurrentPanel"])
  1135. ? app.Settings["CurrentPanel"].Split([" / "], StringSplitOptions.None)
  1136. : [ "Human Resources", "Task List"]
  1137. : [ "Human Resources", "Task List"];
  1138. try
  1139. {
  1140. var bFound = false;
  1141. if (module.Length == 2)
  1142. foreach (Fluent.RibbonTabItem tab in _ribbon.Tabs)
  1143. {
  1144. if (String.Equals(tab.Header, module.First()))
  1145. {
  1146. _ribbon.SelectedTabItem = tab;
  1147. foreach (Fluent.RibbonGroupBox bar in tab.Groups)
  1148. {
  1149. foreach (var item in bar.Items)
  1150. {
  1151. var button = item as Fluent.Button;
  1152. if (button != null && String.Equals(button.Header, module.Last()))
  1153. {
  1154. bFound = true;
  1155. if (button.Command is SimpleCommand command)
  1156. command.Execute(null);
  1157. //button.RaiseEvent(new RoutedEventArgs(ButtonBase.ClickEvent));
  1158. break;
  1159. }
  1160. }
  1161. if (bFound)
  1162. break;
  1163. }
  1164. }
  1165. if (bFound)
  1166. break;
  1167. }
  1168. }
  1169. catch (Exception e)
  1170. {
  1171. MessageWindow.ShowError($"Unable to load {app.Settings["CurrentPanel"]}", e);
  1172. }
  1173. }
  1174. private void _ribbon_OnLoaded(object sender, RoutedEventArgs e)
  1175. {
  1176. _ribbon.SelectedTabItem = CurrentTab;
  1177. }
  1178. private void LoadSecondaryWindows()
  1179. {
  1180. if (ClientFactory.UserGuid != Guid.Empty)
  1181. {
  1182. var windows = App.DatabaseSettings.SecondaryWindows;
  1183. foreach (var key in windows.Keys.ToArray())
  1184. {
  1185. SecondaryWindows[key] = new SecondaryWindow(
  1186. key,
  1187. windows[key].Item1,
  1188. windows[key].Item2,
  1189. windows[key].Item3,
  1190. windows[key].Item4,
  1191. windows[key].Item5,
  1192. windows[key].Item6
  1193. );
  1194. SecondaryWindows[key].Closed += (o, e) => { SecondaryWindows.Remove(key); };
  1195. SecondaryWindows[key].Show();
  1196. }
  1197. }
  1198. else
  1199. {
  1200. foreach (var key in SecondaryWindows.Keys.ToArray())
  1201. {
  1202. App.IsClosing = true;
  1203. SecondaryWindows[key].Close();
  1204. App.IsClosing = false;
  1205. }
  1206. }
  1207. }
  1208. private Fluent.RibbonTabItem GetTabItem(FrameworkElement? sender)
  1209. {
  1210. if (sender == null)
  1211. throw new Exception("No Tab Found!");
  1212. if (sender is Fluent.RibbonTabItem)
  1213. return (Fluent.RibbonTabItem)sender;
  1214. return GetTabItem(sender.Parent as FrameworkElement);
  1215. }
  1216. private IEnumerable<IBasePanel> Panels
  1217. {
  1218. get
  1219. {
  1220. if(PanelHost.CurrentPanel is not null)
  1221. {
  1222. yield return PanelHost.CurrentPanel;
  1223. }
  1224. foreach(var window in SecondaryWindows.Values)
  1225. {
  1226. yield return window.Panel;
  1227. }
  1228. }
  1229. }
  1230. #region LoadWindow / LoadSecondaryWindow
  1231. private T? LoadWindow<T>(Fluent.Button sender) where T : class, IBasePanel, new()
  1232. {
  1233. return LoadWindow<T>(sender, new CancelEventArgs());
  1234. }
  1235. public IBasePanel? LoadWindow(Type t, Fluent.Button sender, CancelEventArgs cancel)
  1236. {
  1237. using (new WaitCursor())
  1238. {
  1239. UnloadWindow(cancel);
  1240. if (cancel.Cancel)
  1241. {
  1242. return null;
  1243. }
  1244. CurrentTab = GetTabItem(sender);
  1245. CurrentButton = sender;
  1246. //CurrentButton.IsSelected = true;
  1247. UpdateRibbonColors();
  1248. var moduleName = sender.Header?.ToString() ?? "";
  1249. var panel = PanelHost.LoadPanel(t, moduleName);
  1250. ContentControl.Content = panel;
  1251. Title =
  1252. $"{moduleName} - {(String.Equals(App.Profile?.ToUpper(), "DEFAULT") ? "PRS Desktop" : App.Profile)} (Release {CoreUtils.GetVersion()})";
  1253. PanelHost.Refresh();
  1254. if (panel is NotificationPanel)
  1255. {
  1256. Logger.Send(LogType.Information, ClientFactory.UserID, "Disabling Heartbeat");
  1257. NotificationsWatchDog.IsEnabled = false;
  1258. Notifications.Visibility = Visibility.Collapsed;
  1259. DockingGrid.ColumnDefinitions[1].Width = new GridLength(0, GridUnitType.Pixel);
  1260. DockingGrid.ColumnDefinitions[2].Width = new GridLength(0, GridUnitType.Pixel);
  1261. }
  1262. else
  1263. {
  1264. ReloadNotifications();
  1265. }
  1266. if (sender != null)
  1267. {
  1268. var settings = new LocalConfiguration<AppSettings>().Load();
  1269. var module = string.Format("{0} / {1}", CurrentTab?.Header, sender.Header);
  1270. if (!settings.Settings.ContainsKey("CurrentPanel") || module != settings.Settings["CurrentPanel"])
  1271. {
  1272. settings.Settings["CurrentPanel"] = module;
  1273. try
  1274. {
  1275. new LocalConfiguration<AppSettings>().Save(settings);
  1276. }
  1277. catch (Exception e)
  1278. {
  1279. Logger.Send(LogType.Error, "", string.Format("*** Unknown Error: {0}\n{1}", e.Message, e.StackTrace));
  1280. }
  1281. }
  1282. }
  1283. return panel;
  1284. }
  1285. }
  1286. private T? LoadWindow<T>(Fluent.Button sender, CancelEventArgs cancel) where T : class, IBasePanel, new()
  1287. {
  1288. return LoadWindow(typeof(T), sender, cancel) as T;
  1289. }
  1290. private void LoadSecondaryWindow(Type t, Fluent.Button sender)
  1291. {
  1292. var id = Guid.NewGuid();
  1293. var window = new Tuple<string, string, double, double, double, double>(
  1294. t.EntityName(),
  1295. sender.Header?.ToString() ?? "",
  1296. Left + 100,
  1297. Top + 100,
  1298. Width - 200,
  1299. Height - 200
  1300. );
  1301. App.DatabaseSettings.SecondaryWindows[id] = window;
  1302. new LocalConfiguration<DatabaseSettings>(App.Profile).Save(App.DatabaseSettings);
  1303. SecondaryWindows[id] = new SecondaryWindow(
  1304. id,
  1305. window.Item1,
  1306. window.Item2,
  1307. window.Item3,
  1308. window.Item4,
  1309. window.Item5,
  1310. window.Item6
  1311. );
  1312. SecondaryWindows[id].Show();
  1313. }
  1314. private void LoadSecondaryWindow<T>(Fluent.Button sender) where T : class, IBasePanel, new()
  1315. => LoadSecondaryWindow(typeof(T), sender);
  1316. // private void SecondaryWindow_Click(object sender, RoutedEventArgs e)
  1317. // {
  1318. // var panel = PanelHost.CurrentPanel;
  1319. // if (panel is null) return;
  1320. //
  1321. // var id = Guid.NewGuid();
  1322. // var window = new Tuple<string, string, double, double, double, double>(
  1323. // panel.GetType().EntityName(),
  1324. // PanelHost.CurrentModuleName,
  1325. // Left + 100,
  1326. // Top + 100,
  1327. // Width - 200,
  1328. // Height - 200
  1329. // );
  1330. // App.DatabaseSettings.SecondaryWindows[id] = window;
  1331. // new LocalConfiguration<DatabaseSettings>(App.Profile).Save(App.DatabaseSettings);
  1332. // SecondaryWindows[id] = new SecondaryWindow(
  1333. // id,
  1334. // window.Item1,
  1335. // window.Item2,
  1336. // window.Item3,
  1337. // window.Item4,
  1338. // window.Item5,
  1339. // window.Item6
  1340. // );
  1341. // SecondaryWindows[id].Show();
  1342. // }
  1343. #endregion
  1344. private void UpdateRibbonColors()
  1345. {
  1346. foreach (var tab in _ribbon.Tabs)
  1347. {
  1348. bool bFound = false;
  1349. foreach (var grp in tab.Groups)
  1350. {
  1351. foreach (var btn in grp.Items)
  1352. {
  1353. if (btn is Fluent.Button fluentbutton)
  1354. {
  1355. bFound = bFound || (btn == CurrentButton);
  1356. fluentbutton.Background = (btn == CurrentButton) ? ThemeManager.SelectedTabItemBackgroundBrush : new SolidColorBrush(Colors.White);
  1357. fluentbutton.Foreground = (btn == CurrentButton) ? ThemeManager.SelectedTabItemForegroundBrush : new SolidColorBrush(Colors.Black);
  1358. }
  1359. }
  1360. tab.Background = bFound ? ThemeManager.SelectedTabItemBackgroundBrush : new SolidColorBrush(Colors.White);
  1361. tab.Foreground = bFound ? ThemeManager.SelectedTabItemForegroundBrush : new SolidColorBrush(Colors.Black);
  1362. }
  1363. }
  1364. }
  1365. private static void StartLocalDatabase(IProgress<string> progress)
  1366. {
  1367. var dirName = Path.GetDirectoryName(App.DatabaseSettings.FileName);
  1368. if (!Directory.Exists(dirName) && dirName != null)
  1369. Directory.CreateDirectory(dirName);
  1370. var FileName = App.DatabaseSettings.FileName;
  1371. var Exists = File.Exists(FileName);
  1372. progress.Report("Configuring Stores");
  1373. DbFactory.Stores = CoreUtils.TypeList(
  1374. new[]
  1375. {
  1376. typeof(Store<>).Assembly,
  1377. typeof(EquipmentStore).Assembly
  1378. },
  1379. myType =>
  1380. myType.IsClass
  1381. && !myType.IsAbstract
  1382. && !myType.IsGenericType
  1383. && myType.GetInterfaces().Contains(typeof(IStore))
  1384. ).ToArray();
  1385. DbFactory.DefaultStore = typeof(BaseStore<>);
  1386. DbFactory.ProviderFactory = new SQLiteProviderFactory(App.DatabaseSettings.FileName);
  1387. DbFactory.ColorScheme = App.DatabaseSettings.ColorScheme;
  1388. DbFactory.Logo = App.DatabaseSettings.Logo;
  1389. progress.Report("Starting Local Database");
  1390. DbFactory.Start();
  1391. ClientFactory.DatabaseID = DbFactory.ID;
  1392. progress.Report("Checking Database");
  1393. var users = DbFactory.NewProvider(Logger.Main).Load<User>();
  1394. if (!users.Any())
  1395. {
  1396. var user = new User { UserID = "ADMIN", Password = "admin" };
  1397. DbFactory.NewProvider(Logger.Main).Save(user);
  1398. var employee = DbFactory.NewProvider(Logger.Main).Load(Filter<Employee>.Where(x => x.Code).IsEqualTo("ADMIN")).FirstOrDefault();
  1399. employee ??= new Employee { Code = "ADMIN", Name = "Administrator Account" };
  1400. employee.UserLink.ID = user.ID;
  1401. DbFactory.NewProvider(Logger.Main).Save(employee);
  1402. }
  1403. CoreUtils.GoogleAPIKey = App.DatabaseSettings.GoogleAPIKey;
  1404. Job.JobNumberPrefix = App.DatabaseSettings.JobPrefix;
  1405. PurchaseOrder.PONumberPrefix = App.DatabaseSettings.PurchaseOrderPrefix;
  1406. }
  1407. #endregion
  1408. #region Login Management
  1409. private ValidationStatus? TryAutoLogin()
  1410. {
  1411. ValidationStatus? result = null;
  1412. if (App.DatabaseSettings.LoginType == LoginType.UserID)
  1413. {
  1414. try
  1415. {
  1416. result = ClientFactory.Validate(App.DatabaseSettings.UserID, App.DatabaseSettings.Password);
  1417. }
  1418. catch (Exception e)
  1419. {
  1420. MessageWindow.ShowError("Error connecting to server.\nPlease check the server URL and port number.", e);
  1421. result = null;
  1422. }
  1423. if (result == ValidationStatus.INVALID)
  1424. {
  1425. MessageWindow.ShowMessage("Unable to Login with User ID: " + App.DatabaseSettings.UserID, "Login failed");
  1426. }
  1427. }
  1428. return result;
  1429. }
  1430. private ValidationStatus? DoLogin()
  1431. {
  1432. ValidationStatus? result = null;
  1433. if (result != ValidationStatus.VALID)
  1434. {
  1435. var login = new PinLogin(CoreUtils.GetVersion(), result ?? ValidationStatus.INVALID);
  1436. if (login.ShowDialog() == true)
  1437. {
  1438. result = ValidationStatus.VALID;
  1439. }
  1440. }
  1441. return result ?? ValidationStatus.INVALID;
  1442. }
  1443. /// <summary>
  1444. /// To be called after <see cref="DoLogin(bool)"/> and if a valid login session exists. Configures the main screen and loads the windows.
  1445. /// </summary>
  1446. /// <param name="progress">If not <see langword="null"/>, then rather than opening a new progress window, just uses that.</param>
  1447. private void AfterLogin(IProgress<string>? progress)
  1448. {
  1449. try
  1450. {
  1451. Logger.Send(LogType.Information, "", "Checking Support Ticket Status");
  1452. CheckSupportTicketStatus();
  1453. Logger.Send(LogType.Information, "", "Loading employee");
  1454. LoadCurrentEmployee();
  1455. if (CheckTimesheetBypass(true))
  1456. {
  1457. UpdateCurrentLogin();
  1458. }
  1459. else
  1460. {
  1461. Dispatcher.Invoke(() =>
  1462. {
  1463. MessageWindow.ShowMessage("You must clock on before logging in to PRS!", "Not clocked in.");
  1464. });
  1465. ClientFactory.InvalidateUser();
  1466. App.EmployeeID = Guid.Empty;
  1467. App.EmployeeName = "";
  1468. App.EmployeeCode = "";
  1469. App.EmployeeEmail = "";
  1470. }
  1471. Logger.Send(LogType.Information, "", "Setting colours");
  1472. if (progress is null)
  1473. {
  1474. ApplyColorScheme();
  1475. }
  1476. else
  1477. {
  1478. Dispatcher.Invoke(ApplyColorScheme);
  1479. }
  1480. Logger.Send(LogType.Information, "", "Configuring main window");
  1481. ConfigureMainScreen(progress);
  1482. Logger.Send(LogType.Information, "", "Loading current window");
  1483. if (progress is null)
  1484. {
  1485. LoadApplicationState();
  1486. }
  1487. else
  1488. {
  1489. Dispatcher.Invoke(LoadApplicationState);
  1490. }
  1491. Logger.Send(LogType.Information, "", "Loading secondary window");
  1492. if (progress is null)
  1493. {
  1494. LoadSecondaryWindows();
  1495. }
  1496. else
  1497. {
  1498. Dispatcher.Invoke(LoadSecondaryWindows);
  1499. }
  1500. }
  1501. catch (Exception e)
  1502. {
  1503. }
  1504. }
  1505. /// <summary>
  1506. /// Creates a new <see cref="Login"/> if one does not already exist. Otherwise, updates the <see cref="Login"/> entry in the database with new Station ID.
  1507. /// </summary>
  1508. private void UpdateCurrentLogin()
  1509. {
  1510. if (CoreUtils.GetVersion().Equals("???"))
  1511. return;
  1512. // Register this station with the Server
  1513. // Later on, the heartbeat will check to make sure
  1514. // that the StationID hasn't changed. If it has,
  1515. // then we've logged in somewhere else and we'll
  1516. // drop out of this station
  1517. var curr = new Client<Login>().Query(
  1518. Filter<Login>.Where(x => x.User.ID).IsEqualTo(ClientFactory.UserGuid),
  1519. null
  1520. ).Rows.FirstOrDefault();
  1521. if (curr != null)
  1522. {
  1523. var c = curr.ToObject<Login>();
  1524. c.StationID = station.StationID;
  1525. station = c;
  1526. }
  1527. else
  1528. {
  1529. station.User.ID = ClientFactory.UserGuid;
  1530. station.TimeStamp = DateTime.Now;
  1531. }
  1532. new Client<Login>().Save(station, "", (o, e) => { });
  1533. }
  1534. private void LoadCurrentEmployee()
  1535. {
  1536. var me = new Client<Employee>().Query(
  1537. Filter<Employee>.Where(x => x.UserLink.ID).IsEqualTo(ClientFactory.UserGuid),
  1538. Columns.None<Employee>().Add(x => x.ID).Add(x=>x.Code).Add(x => x.Email).Add(x => x.Name)
  1539. );
  1540. App.EmployeeID = me.Rows.FirstOrDefault()?.Get<Employee, Guid>(x => x.ID) ?? Guid.Empty;
  1541. App.EmployeeCode = me.Rows.FirstOrDefault()?.Get<Employee, string>(x => x.Code) ?? "";
  1542. App.EmployeeName = me.Rows.FirstOrDefault()?.Get<Employee, String>(x => x.Name) ?? "";
  1543. App.EmployeeCode = me.Rows.FirstOrDefault()?.Get<Employee, String>(x => x.Code) ?? "";
  1544. App.EmployeeEmail = me.Rows.FirstOrDefault()?.Get<Employee, String>(x => x.Email) ?? "";
  1545. }
  1546. private void ExecuteLogout()
  1547. {
  1548. new Client<Login>().Delete(station, "");
  1549. station.ID = Guid.Empty;
  1550. App.EmployeeID = Guid.Empty;
  1551. App.EmployeeName = "";
  1552. App.EmployeeEmail = "";
  1553. }
  1554. /// <summary>
  1555. /// Logs the user out and unloads windows
  1556. /// </summary>
  1557. /// <param name="message">A message to display as the reason for logging out, or <c>null</c> for no message.</param>
  1558. private bool Logout(string? message = null, bool force = false)
  1559. {
  1560. // 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,
  1561. // all the queries that get called here will throw exceptions and thus break our system, failing to log out.
  1562. try
  1563. {
  1564. FinalizeAutoTimesheet();
  1565. }
  1566. catch
  1567. {
  1568. if (!force) throw;
  1569. }
  1570. // Try to unload the window;
  1571. try
  1572. {
  1573. UnloadWindow(null);
  1574. if (DatabaseType == DatabaseType.Standalone && !CoreUtils.GetVersion().Equals("???"))
  1575. scheduler.Stop();
  1576. }
  1577. catch
  1578. {
  1579. if (!force) throw;
  1580. }
  1581. // Next, try to set things to being empty
  1582. try
  1583. {
  1584. if (!CoreUtils.GetVersion().Equals("???"))
  1585. if (station.ID != Guid.Empty)
  1586. {
  1587. ExecuteLogout();
  1588. }
  1589. ClearTrackingKanban();
  1590. }
  1591. catch
  1592. {
  1593. if (!force) throw;
  1594. }
  1595. ClientFactory.InvalidateUser();
  1596. ConfigureMainScreen(null);
  1597. LoadSecondaryWindows();
  1598. if (message != null)
  1599. {
  1600. MessageWindow.ShowMessage(message, "Logged out");
  1601. }
  1602. if (DoLogin() == ValidationStatus.VALID)
  1603. {
  1604. AfterLogin(null);
  1605. }
  1606. return true;
  1607. }
  1608. #endregion
  1609. #region Timesheets
  1610. private void RefreshTimeSheets()
  1611. {
  1612. if (App.EmployeeID == Guid.Empty)
  1613. return;
  1614. var filter = Filter<TimeSheet>.Where(x => x.EmployeeLink.ID).IsEqualTo(App.EmployeeID);
  1615. filter = filter.And(Filter<TimeSheet>.Where(x => x.Confirmed).IsEqualTo(DateTime.MinValue).Or(x => x.Date).IsEqualTo(DateTime.Today));
  1616. _timesheets = new Client<TimeSheet>().Query(
  1617. filter,
  1618. Columns.None<TimeSheet>().Add(
  1619. x => x.ID,
  1620. x => x.Date,
  1621. x => x.Finish
  1622. )
  1623. );
  1624. }
  1625. private CoreTable GetTimesheet()
  1626. {
  1627. return new Client<TimeSheet>().Query(
  1628. Filter<TimeSheet>.Where(x => x.Date).IsEqualTo(DateTime.Today)
  1629. .And(x => x.EmployeeLink.ID).IsEqualTo(App.EmployeeID)
  1630. .And(x => x.Finish).IsEqualTo(TimeSpan.Zero)
  1631. );
  1632. }
  1633. private bool CheckTimesheetBypass(bool message)
  1634. {
  1635. var isClockedOn = IsClockedOn();
  1636. if (!Security.IsAllowed<CanBypassTimeBench>())
  1637. {
  1638. if (!isClockedOn)
  1639. {
  1640. return false;
  1641. }
  1642. return true;
  1643. }
  1644. if (Security.IsAllowed<AutoGenerateTimesheet>())
  1645. if (!isClockedOn)
  1646. {
  1647. var ts = new TimeSheet();
  1648. ts.Date = DateTime.Today;
  1649. ts.Start = DateTime.Now.TimeOfDay;
  1650. ts.EmployeeLink.ID = App.EmployeeID;
  1651. ts.Notes = "Automatic Login from PRS Desktop";
  1652. new Client<TimeSheet>().Save(ts, "AutoLogon because Timebench Bypass is enabled", (o, e) => { });
  1653. }
  1654. return true;
  1655. }
  1656. private bool IsClockedOn()
  1657. {
  1658. RefreshTimeSheets();
  1659. if (_timesheets == null)
  1660. return false;
  1661. return _timesheets.Rows.Any(r =>
  1662. r.Get<TimeSheet, DateTime>(c => c.Date).Date == DateTime.Today && r.Get<TimeSheet, TimeSpan>(c => c.Finish) == new TimeSpan());
  1663. }
  1664. private void FinalizeAutoTimesheet()
  1665. {
  1666. if (Security.IsAllowed<AutoGenerateTimesheet>())
  1667. {
  1668. var check = new Client<TimeSheet>().Query(
  1669. Filter<TimeSheet>.Where(x => x.Date).IsEqualTo(DateTime.Today).And(x => x.EmployeeLink.ID).IsEqualTo(App.EmployeeID).And(x => x.Finish)
  1670. .IsEqualTo(TimeSpan.Zero)
  1671. );
  1672. if (check.Rows.Any())
  1673. {
  1674. var ts = check.Rows.First().ToObject<TimeSheet>();
  1675. if (DateTime.Now.TimeOfDay < ts.Start.Add(new TimeSpan(0, 2, 0)))
  1676. {
  1677. new Client<TimeSheet>().Delete(ts, "Deleting Auto TimeSheet because TimeBench Bypass is enabled", (o, ex) => { });
  1678. }
  1679. else
  1680. {
  1681. ts.Finish = DateTime.Now.TimeOfDay;
  1682. new Client<TimeSheet>().Save(ts, "Clocking off Auto Timesheet because Timesheet Bypass is enabled", (o, ex) => { });
  1683. }
  1684. }
  1685. }
  1686. }
  1687. #endregion
  1688. private string CurrentPanelSlug()
  1689. {
  1690. var app = new LocalConfiguration<AppSettings>().Load();
  1691. var module = app.Settings["CurrentPanel"].Split(new[] { " / " }, StringSplitOptions.None);
  1692. return module.LastOrDefault()?.Replace(" ", "_").Replace("/", "") ?? "";
  1693. }
  1694. private bool ShowHelp()
  1695. {
  1696. Process.Start(new ProcessStartInfo("https://prsdigital.com.au/wiki/index.php/" + CurrentPanelSlug()) { UseShellExecute = true });
  1697. return true;
  1698. }
  1699. private void Wiki_Click(object sender, RoutedEventArgs e)
  1700. {
  1701. ShowHelp();
  1702. }
  1703. private void Window_Loaded(object sender, RoutedEventArgs e)
  1704. {
  1705. }
  1706. private void UnloadWindow(CancelEventArgs? cancel)
  1707. {
  1708. PanelHost.UnloadPanel(cancel);
  1709. if (cancel?.Cancel == true)
  1710. {
  1711. return;
  1712. }
  1713. Title =
  1714. $" - {(String.Equals(App.Profile?.ToUpper(), "DEFAULT") ? "PRS Desktop" : App.Profile)} (Release {CoreUtils.GetVersion()})";
  1715. if (CurrentTab is not null)
  1716. {
  1717. var border = VisualUtils.EnumChildrenOfType(CurrentTab, typeof(Border)).LastOrDefault();
  1718. if (border != null)
  1719. {
  1720. ((Border)border).Background = new SolidColorBrush(Colors.Transparent);
  1721. ((Border)border).BorderBrush = new SolidColorBrush(Colors.Transparent);
  1722. }
  1723. var ReportsBar = FindRibbonBar(CurrentTab, x => x.Header.Equals("Print"));
  1724. if (ReportsBar is not null)
  1725. {
  1726. ReportsBar.Items.Clear();
  1727. ReportsBar.Visibility = Visibility.Collapsed;
  1728. ReportsBar.IsLauncherVisible = false;
  1729. }
  1730. var ActionBar = FindRibbonBar(CurrentTab, x => x.Header.Equals("Actions"));
  1731. if (ActionBar is not null)
  1732. {
  1733. ActionBar.IsLauncherVisible = false;
  1734. foreach (var module in CurrentModules)
  1735. ActionBar.Items.Remove(module);
  1736. }
  1737. }
  1738. CurrentTab = null;
  1739. CurrentButton = null;
  1740. ContentControl.Content = null;
  1741. }
  1742. private void RibbonWindow_Activated(object sender, EventArgs e)
  1743. {
  1744. }
  1745. private void RegisterModules(IProgress<string> progress)
  1746. {
  1747. foreach (Fluent.RibbonTabItem tab in _ribbon.Tabs)
  1748. foreach (Fluent.RibbonGroupBox bar in tab.Groups)
  1749. foreach (var item in bar.Items.OfType<RibbonButton>())
  1750. Dispatcher.Invoke(() =>
  1751. {
  1752. if (item.Label != "Refresh")
  1753. if (bar.Header.Equals("Actions"))
  1754. Modules.Register(item.Label);
  1755. });
  1756. }
  1757. private static Fluent.RibbonGroupBox? FindRibbonBar(Fluent.RibbonTabItem tab, Func<Fluent.RibbonGroupBox, bool> predicate)
  1758. {
  1759. foreach (var group in tab.Groups)
  1760. {
  1761. if (group != null)
  1762. if (predicate.Invoke(group))
  1763. return group;
  1764. }
  1765. return null;
  1766. }
  1767. #region Button Event Handlers
  1768. #region Accounts
  1769. #endregion
  1770. #region Dashboards
  1771. #endregion
  1772. private void Console_Click(object sender, RoutedEventArgs a)
  1773. {
  1774. if (_console is null)
  1775. {
  1776. _console = new DesktopConsole("Console");
  1777. _console.Closing += (o, args) => _console = null;
  1778. }
  1779. _console.Show();
  1780. }
  1781. private void RefreshMenu_Click(object sender, RoutedEventArgs e)
  1782. {
  1783. PanelHost.Refresh();
  1784. }
  1785. //private void NotificationsList_SelectionChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e)
  1786. //{
  1787. // if (NotificationsList.SelectedIndex < 0)
  1788. // return;
  1789. // var editors = NotificationsList.FindVisualChildren<InABox.DynamicGrid.ExtendedRichTextEditor>().ToArray();
  1790. // var selected = editors[NotificationsList.SelectedIndex];
  1791. // selected.Text = (String)selected.Tag; //NotificationsList.SelectedIndex.ToString();
  1792. //}
  1793. //private void ViewNotification_Click(object sender, System.Windows.Input.MouseButtonEventArgs e)
  1794. //{
  1795. // Notification notification = (sender as Label).Tag as Notification;
  1796. // NotificationDetails details = new NotificationDetails(notification);
  1797. // details.ShowDialog();
  1798. // ReloadNotifications();
  1799. //}
  1800. private void Library_Click(object sender, RoutedEventArgs e)
  1801. {
  1802. Process.Start(
  1803. new ProcessStartInfo
  1804. {
  1805. FileName = App.DatabaseSettings.LibraryLocation,
  1806. UseShellExecute = true,
  1807. Verb = "open"
  1808. }
  1809. );
  1810. }
  1811. private void SendNotificationClick(object sender, RoutedEventArgs e)
  1812. {
  1813. var form = new NotificationForm { Description = "" };
  1814. if (form.ShowDialog() == true)
  1815. ReloadNotifications();
  1816. }
  1817. private void CompanyInformation_Click(object sender, RoutedEventArgs e)
  1818. {
  1819. var info = new Client<CompanyInformation>().Load().FirstOrDefault();
  1820. if (info == null)
  1821. info = new CompanyInformation();
  1822. new DynamicDataGrid<CompanyInformation>().EditItems(new[] { info });
  1823. }
  1824. private void Setup_Click(object sender, RoutedEventArgs e)
  1825. {
  1826. var tab = _ribbon.SelectedTabItem;
  1827. if (tab is null)
  1828. return;
  1829. var menu = new ContextMenu();
  1830. PanelHost.InitialiseSetupMenu(menu);
  1831. menu.IsOpen = true;
  1832. }
  1833. private void Issues_Click(object sender, RoutedEventArgs e)
  1834. {
  1835. try
  1836. {
  1837. IssuesWindow.Execute();
  1838. CheckSupportTicketStatus();
  1839. }
  1840. catch(Exception err)
  1841. {
  1842. MessageWindow.ShowError("Could not load issues.", err);
  1843. }
  1844. }
  1845. private void Mobile_Click(object sender, RoutedEventArgs args)
  1846. {
  1847. var _baseDirectory = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location) ?? "";
  1848. var _mobileApp = System.IO.Path.Combine(_baseDirectory, "PRSAvalonia", "PRS.Avalonia.Desktop.exe");
  1849. var _info = new ProcessStartInfo(_mobileApp);
  1850. Process.Start(_info);
  1851. }
  1852. private void StartForm<TEntityForm, TEntity, TEntityLink>(DigitalForm form)
  1853. where TEntityForm : BaseEntityForm<TEntity, TEntityLink, TEntityForm>, new()
  1854. where TEntity : Entity, new()
  1855. where TEntityLink : EntityLink<TEntity>, new()
  1856. {
  1857. var entityForm = new TEntityForm();
  1858. entityForm.Form.CopyFrom(form);
  1859. entityForm.Description = form.Description;
  1860. entityForm.FormStarted = DateTime.Now;
  1861. var entity = DFUtils.NewEntity<TEntityForm, TEntity, TEntityLink>(form);
  1862. if (DynamicFormEditWindow.EditDigitalForm(entityForm, out var dataModel, entity))
  1863. {
  1864. dataModel.Update(null);
  1865. }
  1866. }
  1867. private void Forms_Click(object sender, RoutedEventArgs e)
  1868. {
  1869. var select = new MultiSelectDialog<DigitalForm>(
  1870. Filter<DigitalForm>.And(
  1871. LookupFactory.DefineChildFilter<KanbanForm, DigitalForm>(Array.Empty<KanbanForm>()),
  1872. Filter<DigitalForm>.Where(x => x.ID).InQuery(
  1873. Filter<EmployeeDigitalForm>.Where(x => x.Employee.ID).IsEqualTo(App.EmployeeID),
  1874. x => x.Form.ID)),
  1875. LookupFactory.DefineChildColumns<KanbanForm, DigitalForm>()
  1876. .Add(x => x.Description),
  1877. false);
  1878. if (select.ShowDialog() == true)
  1879. {
  1880. var digitalForm = select.Data().Rows.FirstOrDefault()?.ToObject<DigitalForm>();
  1881. if (digitalForm is not null)
  1882. {
  1883. StartForm<KanbanForm, Kanban, KanbanLink>(digitalForm);
  1884. }
  1885. };
  1886. }
  1887. #endregion
  1888. private bool OutstandingDailyReports(bool refresh)
  1889. {
  1890. if (!Security.IsAllowed<CanViewDailyReports>() || Security.IsAllowed<BypassOutstandingDailyReports>())
  1891. return false;
  1892. if (refresh)
  1893. RefreshTimeSheets();
  1894. if (_timesheets == null)
  1895. return false;
  1896. return _timesheets.Rows.Any(r => r.Get<TimeSheet, DateTime>(c => c.Date).Date < DateTime.Today);
  1897. }
  1898. private void ShutDownTransport()
  1899. {
  1900. if (_transport != null)
  1901. {
  1902. _transport.OnClose -= TransportConnectionLost;
  1903. if (_transport.IsConnected())
  1904. _transport.Disconnect();
  1905. _transport = null;
  1906. }
  1907. }
  1908. private void Window_Unloaded(object sender, RoutedEventArgs e)
  1909. {
  1910. ShutDownTransport();
  1911. }
  1912. private void RibbonWindow_Closed(object sender, EventArgs e)
  1913. {
  1914. ShutDownTransport();
  1915. //DisconnectRecorderNotes();
  1916. Application.Current.Shutdown();
  1917. }
  1918. //private bool _closingFromSystemMenu = false;
  1919. private void Window_Closing(object sender, CancelEventArgs e)
  1920. {
  1921. /*if (!_closingFromSystemMenu && !CoreUtils.GetVersion().Equals("???"))
  1922. {
  1923. WindowState = WindowState.Minimized;
  1924. e.Cancel = true;
  1925. return;
  1926. }*/
  1927. PanelHost.UnloadPanel(e);
  1928. if (!e.Cancel)
  1929. {
  1930. ISubPanelHost.Global.ShutdownSubPanels(e);
  1931. }
  1932. if (e.Cancel)
  1933. {
  1934. return;
  1935. }
  1936. App.IsClosing = true;
  1937. FinalizeAutoTimesheet();
  1938. if (!CoreUtils.GetVersion().Equals("???"))
  1939. scheduler.Stop();
  1940. CurrentTab = null;
  1941. UpdateRibbonColors();
  1942. if (!CoreUtils.GetVersion().Equals("???"))
  1943. if (station.ID != Guid.Empty)
  1944. ExecuteLogout();
  1945. ShutDownTransport();
  1946. }
  1947. #region Notifications + Heartbeat
  1948. private void ReceiveNotification(Notification notification)
  1949. {
  1950. if (Security.CanView<Notification>())
  1951. {
  1952. Notifications.AddNotification(notification);
  1953. }
  1954. foreach(var panel in Panels.OfType<NotificationPanel>())
  1955. {
  1956. panel.AddNotification(notification);
  1957. }
  1958. }
  1959. private void Notifications_Tick(object? sender, EventArgs e)
  1960. {
  1961. if (ClientFactory.UserGuid != Guid.Empty)
  1962. {
  1963. try
  1964. {
  1965. ReloadNotifications();
  1966. }
  1967. catch (Exception err)
  1968. {
  1969. Logger.Send(LogType.Error, ClientFactory.UserID,
  1970. string.Format("Exception in Notifications_Tick:ReloadNotifications() {0}\n{1}", err.Message, err.StackTrace));
  1971. }
  1972. Heartbeat();
  1973. try
  1974. {
  1975. CheckIsLoggedOn();
  1976. }
  1977. catch (Exception err2)
  1978. {
  1979. Logger.Send(LogType.Error, ClientFactory.UserID,
  1980. string.Format("Exception in Notifications_Tick:CheckIsLoggedOn() {0}\n{1}", err2.Message, err2.StackTrace));
  1981. }
  1982. }
  1983. //else
  1984. //{
  1985. // Logger.Send(LogType.Information, ClientFactory.UserID, "Notifications_Tick: ClientFactory.UserGuid is empty");
  1986. //}
  1987. }
  1988. private void CheckIsLoggedOn()
  1989. {
  1990. if (CoreUtils.GetVersion().Equals("???"))
  1991. return;
  1992. var bLogout = false;
  1993. if (!Security.IsAllowed<CanBypassTimeBench>())
  1994. if (!IsClockedOn())
  1995. {
  1996. Logger.Send(LogType.Information, ClientFactory.UserID, "User is no longer clocked in!");
  1997. bLogout = true;
  1998. Dispatcher.Invoke(() => { Logout(); });
  1999. }
  2000. if (!bLogout)
  2001. new Client<Login>().Query(
  2002. Filter<Login>.Where(x => x.User.ID).IsEqualTo(ClientFactory.UserGuid),
  2003. Columns.None<Login>().Add(x => x.StationID),
  2004. null,
  2005. CoreRange.All,
  2006. (o, e) =>
  2007. {
  2008. if (e is RemoteException remote)
  2009. {
  2010. if (remote.Status == StatusCode.Unauthenticated)
  2011. {
  2012. Logger.Send(LogType.Information, ClientFactory.UserID, "User has been logged out");
  2013. Dispatcher.Invoke(() =>
  2014. {
  2015. Logout("You have been logged out due to inactivity");
  2016. });
  2017. }
  2018. else
  2019. {
  2020. Logger.Send(LogType.Information, ClientFactory.UserID, $"Error in CheckIsLoggedOn(): {CoreUtils.FormatException(remote)}");
  2021. }
  2022. }
  2023. else if (e is not null)
  2024. {
  2025. Logger.Send(LogType.Information, ClientFactory.UserID, $"Error in CheckIsLoggedOn(): {CoreUtils.FormatException(e)}");
  2026. }
  2027. else if (o is not null)
  2028. {
  2029. var row = o.Rows.FirstOrDefault();
  2030. if (row == null)
  2031. {
  2032. station.ID = Guid.Empty;
  2033. new Client<Login>().Save(station, "", (o1, e1) => { });
  2034. }
  2035. else if (!row.Get<Login, string>(c => c.StationID).Equals(station.StationID))
  2036. {
  2037. Logger.Send(LogType.Information, ClientFactory.UserID, "User logged in somewhere else!");
  2038. bLogout = true;
  2039. Dispatcher.Invoke(() => { Logout(); });
  2040. }
  2041. }
  2042. }
  2043. );
  2044. }
  2045. private void Heartbeat()
  2046. {
  2047. //Task.Run(() =>
  2048. //{
  2049. try
  2050. {
  2051. CheckSupportTicketStatus();
  2052. bool IsClockedOn = this.IsClockedOn();
  2053. if (IsClockedOn)
  2054. {
  2055. if ((_kanbantrackingassignment != null) && (_kanbantrackingassignment.Actual.Finish < DateTime.Now.TimeOfDay))
  2056. {
  2057. _kanbantrackingassignment.Actual.Finish = DateTime.Now.TimeOfDay;
  2058. new Client<Assignment>().Save(_kanbantrackingassignment, "");
  2059. }
  2060. if (Security.IsAllowed<MonitorApplicationWindows>())
  2061. {
  2062. if (ActivityHistory == null)
  2063. ActivityHistory = new LocalConfiguration<DailyActivityHistory>().Load();
  2064. var appname = OpenWindowGetter.GetActiveWindowProcess();
  2065. var title = OpenWindowGetter.GetActiveWindowTitle();
  2066. ActivityHistory.Activities[DateTime.Now] = (!string.IsNullOrWhiteSpace(appname) ? appname.Trim() + " - " : "") + title;
  2067. new LocalConfiguration<DailyActivityHistory>().Save(ActivityHistory);
  2068. }
  2069. }
  2070. PanelHost.Heartbeat();
  2071. foreach(var window in SecondaryWindows.Values)
  2072. {
  2073. window.Heartbeat();
  2074. }
  2075. }
  2076. catch (Exception err)
  2077. {
  2078. Logger.Send(LogType.Error, ClientFactory.UserID, string.Format("Exception in Heartbeat: {0}\n{1}", err.Message, err.StackTrace));
  2079. }
  2080. //});
  2081. }
  2082. private void CheckSupportTicketStatus()
  2083. {
  2084. Dispatcher.BeginInvoke(() =>
  2085. {
  2086. try
  2087. {
  2088. IssuesButton.Background = IssuesWindow.Check()
  2089. ? new SolidColorBrush(Colors.Red) { Opacity = 0.5 }
  2090. : Brushes.Transparent;
  2091. }
  2092. catch (Exception e)
  2093. {
  2094. }
  2095. });
  2096. }
  2097. private void Notifications_Changed(object sender)
  2098. {
  2099. if (Notifications.IsActive)
  2100. {
  2101. Notifications.Visibility = Visibility.Visible;
  2102. DockingGrid.ColumnDefinitions[1].Width = new GridLength(4, GridUnitType.Pixel);
  2103. DockingGrid.ColumnDefinitions[2].Width = Equals(0.0, DockingGrid.ColumnDefinitions[2].Width.Value)
  2104. ? new GridLength(300, GridUnitType.Pixel)
  2105. : DockingGrid.ColumnDefinitions[2].Width;
  2106. }
  2107. else
  2108. {
  2109. Notifications.Visibility = Visibility.Collapsed;
  2110. DockingGrid.ColumnDefinitions[1].Width = new GridLength(0, GridUnitType.Pixel);
  2111. DockingGrid.ColumnDefinitions[2].Width = new GridLength(0, GridUnitType.Pixel);
  2112. }
  2113. }
  2114. private void ReloadNotifications()
  2115. {
  2116. if (Security.CanView<Notification>())
  2117. Notifications.Refresh();
  2118. if (!NotificationsWatchDog.IsEnabled)
  2119. {
  2120. //Logger.Send(LogType.Information, ClientFactory.UserID, "Enabling Heartbeat");
  2121. NotificationsWatchDog.IsEnabled = true;
  2122. }
  2123. }
  2124. #endregion
  2125. private void RibbonWindow_PreviewMouseUp(object sender, MouseButtonEventArgs e)
  2126. {
  2127. PanelHost.IncrementTrackingModuleClick();
  2128. }
  2129. private void RibbonWindow_PreviewKeyUp(object sender, KeyEventArgs e)
  2130. {
  2131. PanelHost.IncrementTrackingModuleKey();
  2132. }
  2133. public static void ActivateWindow(Window window)
  2134. {
  2135. var hwnd = new WindowInteropHelper(window).EnsureHandle();
  2136. var threadId1 = GetWindowThreadProcessId(GetForegroundWindow(), IntPtr.Zero);
  2137. var threadId2 = GetWindowThreadProcessId(hwnd, IntPtr.Zero);
  2138. if (threadId1 != threadId2)
  2139. {
  2140. AttachThreadInput(threadId1, threadId2, true);
  2141. SetForegroundWindow(hwnd);
  2142. AttachThreadInput(threadId1, threadId2, false);
  2143. }
  2144. else
  2145. {
  2146. SetForegroundWindow(hwnd);
  2147. }
  2148. }
  2149. private static IntPtr GetForegroundWindow()
  2150. {
  2151. var active = GetActiveWindow();
  2152. var window = Application.Current.Windows.OfType<Window>().SingleOrDefault(x => new WindowInteropHelper(x).Handle == active);
  2153. var hwnd = new WindowInteropHelper(window).EnsureHandle();
  2154. return hwnd;
  2155. }
  2156. [DllImport("user32.dll", SetLastError = true)]
  2157. private static extern IntPtr SetForegroundWindow(IntPtr hWnd);
  2158. [DllImport("user32.dll")]
  2159. private static extern uint GetWindowThreadProcessId(IntPtr hWnd, IntPtr ProcessId);
  2160. [DllImport("user32.dll")]
  2161. private static extern bool AttachThreadInput(uint idAttach, uint idAttachTo, bool fAttach);
  2162. [DllImport("user32.dll")]
  2163. private static extern IntPtr GetActiveWindow();
  2164. #region Modules + Reports
  2165. public void ClearActions()
  2166. {
  2167. foreach (var module in CurrentModules)
  2168. {
  2169. if (module.Parent is Fluent.RibbonGroupBox bar)
  2170. bar.Items.Remove(module);
  2171. }
  2172. CurrentModules.Clear();
  2173. }
  2174. public void ClearReports()
  2175. {
  2176. if (CurrentTab is not null)
  2177. {
  2178. var ReportsBar = FindRibbonBar(CurrentTab, x => x.Header.Equals("Print"));
  2179. if (ReportsBar is not null)
  2180. {
  2181. ReportsBar.Visibility = Visibility.Collapsed;
  2182. ReportsBar.Items.Clear();
  2183. }
  2184. }
  2185. }
  2186. public void CreateReport(PanelAction action)
  2187. {
  2188. if (CurrentTab is null)
  2189. return;
  2190. var ReportsBar = FindRibbonBar(CurrentTab, x => x.Header.Equals("Print"));
  2191. if (ReportsBar is not null)
  2192. {
  2193. var button = new Fluent.Button
  2194. {
  2195. Header = action.Caption,
  2196. LargeIcon = action.Image?.AsBitmapImage(),
  2197. MinWidth = 60
  2198. };
  2199. if (action.Menu is not null)
  2200. {
  2201. button.ContextMenu = action.Menu;
  2202. }
  2203. button.Click += (o, e) =>
  2204. {
  2205. action.Execute();
  2206. };
  2207. ReportsBar.Visibility = Security.IsAllowed<CanPrintReports>() ? Visibility.Visible : Visibility.Collapsed;
  2208. ReportsBar.Items.Add(button);
  2209. }
  2210. }
  2211. private NullConverter<Visibility>? _vis = null;
  2212. public void CreatePanelAction(PanelAction action)
  2213. {
  2214. if (CurrentTab is null)
  2215. return;
  2216. if (_vis == null)
  2217. {
  2218. _vis = new();
  2219. _vis.Converting += (o, e) =>
  2220. {
  2221. };
  2222. }
  2223. var Actions = FindRibbonBar(CurrentTab, x => x.Header.Equals("Actions"));
  2224. if (Actions is not null)
  2225. {
  2226. if (!CurrentModules.Any(x => x.GetType().Equals(typeof(RibbonSeparator))))
  2227. {
  2228. var sep = new RibbonSeparator();
  2229. CurrentModules.Add(sep);
  2230. Actions.Items.Add(sep);
  2231. }
  2232. if (action.OnPopulate != null)
  2233. {
  2234. Fluent.DropDownButton button;
  2235. if (action.OnExecute != null)
  2236. {
  2237. button = new Fluent.SplitButton();
  2238. ((Fluent.SplitButton)button).Click += (o, e) => action.Execute();
  2239. }
  2240. else
  2241. button = new Fluent.DropDownButton();
  2242. button.MinWidth = 60;
  2243. button.DataContext = action;
  2244. button.DropDownOpened += (sender, args) => { button.ItemsSource = CreateMenuItems(action); };
  2245. button.Bind<PanelAction, String>(Fluent.DropDownButton.HeaderProperty, x => x.Caption, null);
  2246. button.Bind<PanelAction, Bitmap?>(Fluent.DropDownButton.LargeIconProperty, x => x.Image,
  2247. new BitmapToBitmapImageConverter());
  2248. button.Bind<PanelAction, ContextMenu?>(ContextMenuProperty, x => x.Menu);
  2249. button.Bind<PanelAction, bool>(IsEnabledProperty, x => x.IsEnabled);
  2250. button.Bind<PanelAction, Visibility>(Fluent.DropDownButton.VisibilityProperty, x => x.Visibility, _vis);
  2251. Actions.Items.Add(button);
  2252. CurrentModules.Add(button);
  2253. }
  2254. else
  2255. {
  2256. var button = new Fluent.Button()
  2257. {
  2258. MinWidth = 60,
  2259. DataContext = action
  2260. };
  2261. button.Bind<PanelAction, String>(Fluent.Button.HeaderProperty, x => x.Caption, null);
  2262. button.Bind<PanelAction, Bitmap?>(Fluent.Button.LargeIconProperty, x => x.Image,
  2263. new BitmapToBitmapImageConverter());
  2264. button.Bind<PanelAction, ContextMenu?>(Fluent.Button.ContextMenuProperty, x => x.Menu);
  2265. button.Bind<PanelAction, bool>(Fluent.Button.IsEnabledProperty, x => x.IsEnabled);
  2266. button.Bind<PanelAction, Visibility>(Fluent.Button.VisibilityProperty, x => x.Visibility, _vis);
  2267. button.Click += (o, e) => action.Execute();
  2268. Actions.Items.Add(button);
  2269. CurrentModules.Add(button);
  2270. }
  2271. }
  2272. }
  2273. private static ObservableCollection<MenuItem> CreateMenuItems(PanelAction action)
  2274. {
  2275. var items = new ObservableCollection<MenuItem>();
  2276. var entries = action.Populate();
  2277. if (entries != null)
  2278. {
  2279. foreach (var entry in entries)
  2280. {
  2281. var item = new Fluent.MenuItem()
  2282. {
  2283. Header = entry.Caption,
  2284. DataContext = entry.Data,
  2285. Icon = entry.Image?.AsBitmapImage()
  2286. };
  2287. item.Click += (o, eventArgs) => entry.Execute();
  2288. items.Add(item);
  2289. }
  2290. }
  2291. return items;
  2292. }
  2293. #endregion
  2294. #region Tracking Kanban
  2295. private class TrackingKanbanFilterItemComponent : DynamicGridFilterComponent<Kanban>
  2296. {
  2297. public TrackingKanbanFilterItemComponent() : base(
  2298. new GlobalConfiguration<CoreFilterDefinitions>(nameof(Kanban)),
  2299. new UserConfiguration<CoreFilterDefinitions>(nameof(Kanban)))
  2300. {
  2301. ButtonText = "Filter";
  2302. }
  2303. public string Text { get; private set; } = "Filter";
  2304. public Bitmap? Image { get; private set; }
  2305. protected override void UpdateButtonText(Bitmap image, string text)
  2306. {
  2307. Text = text;
  2308. Image = image;
  2309. }
  2310. }
  2311. private TrackingKanbanFilterItemComponent? _trackingKanbanFilterComponent;
  2312. private TrackingKanbanFilterItemComponent TrackingKanbanFilterComponent
  2313. {
  2314. get
  2315. {
  2316. if(_trackingKanbanFilterComponent is null)
  2317. {
  2318. _trackingKanbanFilterComponent = new();
  2319. _trackingKanbanFilterComponent.OnFiltersSelected += _trackingKanbanFilterComponent_OnFiltersSelected;
  2320. }
  2321. return _trackingKanbanFilterComponent;
  2322. }
  2323. }
  2324. private ContextMenu? _trackingKanbanMenu;
  2325. private MenuItem? _trackingKanbanFilterMenu;
  2326. private void _trackingKanbanFilterComponent_OnFiltersSelected(DynamicGridSelectedFilterSettings filters)
  2327. {
  2328. if (_trackingKanbanMenu is null) return;
  2329. if (!filters.MultipleFilters)
  2330. {
  2331. _trackingKanbanMenu.IsOpen = false;
  2332. }
  2333. if(_trackingKanbanFilterMenu is not null)
  2334. {
  2335. _trackingKanbanFilterMenu.Header = TrackingKanbanFilterComponent.Text;
  2336. _trackingKanbanFilterMenu.Icon = new System.Windows.Controls.Image() { Source = TrackingKanbanFilterComponent.Image?.AsBitmapImage(24, 24) };
  2337. }
  2338. }
  2339. private void SelectTask_Click(object sender, RoutedEventArgs e)
  2340. {
  2341. var menu = new ContextMenu();
  2342. var others = new MenuItem() { Header = "Other Tasks" };
  2343. var waiting = new MenuItem() { Header = "Waiting Tasks" };
  2344. using (new WaitCursor())
  2345. {
  2346. var filter =
  2347. Filter<KanbanSubscriber>.Where(x => x.Employee.UserLink.ID).IsEqualTo(ClientFactory.UserGuid)
  2348. .And(x => x.Kanban.Completed).IsEqualTo(DateTime.MinValue)
  2349. .And(x => x.Kanban.Closed).IsEqualTo(DateTime.MinValue);
  2350. var kanbanFilter = TrackingKanbanFilterComponent.GetFilter();
  2351. if(kanbanFilter is not null)
  2352. {
  2353. filter = filter.And(x => x.Kanban.ID).InQuery(kanbanFilter, x => x.ID);
  2354. }
  2355. var kanbans = Client.Query(
  2356. filter,
  2357. Columns.None<KanbanSubscriber>()
  2358. .Add(x => x.Kanban.ID)
  2359. .Add(x => x.Kanban.Number)
  2360. .Add(x => x.Kanban.Title)
  2361. .Add(x => x.Kanban.Status)
  2362. .Add(x => x.Assignee),
  2363. new SortOrder<KanbanSubscriber>(x => x.Kanban.Number, SortDirection.Ascending));
  2364. foreach (var subscriber in kanbans.ToObjects<KanbanSubscriber>())
  2365. {
  2366. CreateTaskMenu(subscriber.Assignee ? (subscriber.Kanban.Status != KanbanStatus.Waiting ? menu : waiting) : others,
  2367. $"{subscriber.Kanban.Number} {subscriber.Kanban.Title}",
  2368. subscriber.Kanban.ID);
  2369. }
  2370. menu.AddSeparatorIfNeeded();
  2371. if(others.Items.Count > 0)
  2372. {
  2373. menu.Items.Add(others);
  2374. }
  2375. if(waiting.Items.Count > 0)
  2376. {
  2377. menu.Items.Add(waiting);
  2378. }
  2379. menu.AddSeparatorIfNeeded();
  2380. CreateTaskMenu(menu, "(No Task Selected)", Guid.Empty);
  2381. if(_kanbantrackingassignment is not null && _kanbantrackingassignment.Task.ID != Guid.Empty)
  2382. {
  2383. menu.AddItem("View Task", null, _kanbantrackingassignment.Task.ID, ViewTrackingKanban_Click);
  2384. }
  2385. menu.AddSeparatorIfNeeded();
  2386. var filterItem = menu.AddItem(TrackingKanbanFilterComponent.Text, TrackingKanbanFilterComponent.Image, null);
  2387. TrackingKanbanFilterComponent.PopulateMenu(filterItem);
  2388. _trackingKanbanFilterMenu = filterItem;
  2389. }
  2390. menu.IsOpen = true;
  2391. _trackingKanbanMenu = menu;
  2392. }
  2393. private void ViewTrackingKanban_Click(Guid guid)
  2394. {
  2395. var item = Client.Query(
  2396. Filter<Kanban>.Where(x => x.ID).IsEqualTo(guid),
  2397. DynamicGridUtils.LoadEditorColumns<Kanban>())
  2398. .ToObjects<Kanban>().FirstOrDefault();
  2399. if (item is null) return;
  2400. var grid = DynamicGridUtils.CreateDynamicGrid<Kanban>(typeof(DynamicGrid<>));
  2401. grid.EditItemsNonModal(ISubPanelHost.Global, [item], (items, saved) => { });
  2402. }
  2403. private Assignment? _kanbantrackingassignment = null;
  2404. private CoreFilterDefinitions? _kanbanTrackingFilter = null;
  2405. private void SetTrackingKanban(Guid kanbanID, string header)
  2406. {
  2407. SelectedTaskName.Content = header;
  2408. var createNewAssignment = false;
  2409. if (_kanbantrackingassignment is not null)
  2410. {
  2411. if (_kanbantrackingassignment.Actual.Finish < DateTime.Now.TimeOfDay)
  2412. {
  2413. _kanbantrackingassignment.Actual.Finish = DateTime.Now.TimeOfDay;
  2414. Client.Save(_kanbantrackingassignment, "");
  2415. }
  2416. // Update Existing Kanban
  2417. if (kanbanID == Guid.Empty)
  2418. _kanbantrackingassignment = null;
  2419. else if (_kanbantrackingassignment.Task.ID != kanbanID)
  2420. {
  2421. createNewAssignment = true;
  2422. }
  2423. }
  2424. else if (kanbanID != Guid.Empty)
  2425. {
  2426. createNewAssignment = true;
  2427. }
  2428. if (createNewAssignment)
  2429. {
  2430. _kanbantrackingassignment = new Assignment();
  2431. _kanbantrackingassignment.Task.ID = kanbanID;
  2432. _kanbantrackingassignment.EmployeeLink.ID = App.EmployeeID;
  2433. _kanbantrackingassignment.Title = header;
  2434. _kanbantrackingassignment.Date = DateTime.Today;
  2435. _kanbantrackingassignment.Actual.Start = DateTime.Now.TimeOfDay;
  2436. _kanbantrackingassignment.Actual.Finish = DateTime.Now.TimeOfDay.Add(new TimeSpan(0, 2, 0));
  2437. Client.Save(_kanbantrackingassignment, "");
  2438. }
  2439. }
  2440. private void ClearTrackingKanban()
  2441. => SetTrackingKanban(Guid.Empty, "(No Task Selected)");
  2442. private void CreateTaskMenu(ItemsControl menu, string title, Guid id)
  2443. {
  2444. menu.AddCheckMenuItem(title, (title, id), TrackingKanbanMenuItem_Click, isChecked: _kanbantrackingassignment?.Task.ID == id);
  2445. }
  2446. private void TrackingKanbanMenuItem_Click((string title, Guid id) tuple, bool isChecked)
  2447. {
  2448. if (isChecked)
  2449. {
  2450. SetTrackingKanban(tuple.id, tuple.title);
  2451. }
  2452. }
  2453. #endregion
  2454. private void DockPanelBorder_IsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
  2455. {
  2456. if (sender is not Border border || !border.IsVisible)
  2457. return;
  2458. var dock = border.Child is IDockPanel panel ? panel : border.Child?.FindVisualChildren<IDockPanel>().FirstOrDefault();
  2459. if (dock is null)
  2460. return;
  2461. dock.Refresh();
  2462. }
  2463. private void DockPanel_OnIsActiveChanged(object? sender, EventArgs e)
  2464. {
  2465. if (sender is not LayoutAnchorable layout)
  2466. return;
  2467. var content = layout.Content as DependencyObject;
  2468. var dock = content is IDockPanel panel ? panel : content?.FindVisualChildren<IDockPanel>().FirstOrDefault();
  2469. if (dock is null)
  2470. return;
  2471. if (layout.IsActive && layout.IsVisible)
  2472. dock.Refresh();
  2473. }
  2474. private void _ribbon_OnPreviewMouseDoubleClick(object sender, MouseButtonEventArgs e)
  2475. {
  2476. e.Handled = true;
  2477. }
  2478. #region Backstage Functions
  2479. private enum DatabaseConfigurationResult
  2480. {
  2481. RestartRequired,
  2482. RestartNotRequired,
  2483. Cancel
  2484. }
  2485. private DatabaseConfigurationResult ShowDatabaseConfiguration()
  2486. {
  2487. DatabaseConfigurationResult result;
  2488. var config = new DataBaseConfiguration();
  2489. if (config.ShowDialog() == true)
  2490. {
  2491. var newsettings = new LocalConfiguration<DatabaseSettings>(App.Profile).Load();
  2492. if (newsettings.RestartRequired(App.DatabaseSettings))
  2493. {
  2494. result = DatabaseConfigurationResult.RestartRequired;
  2495. }
  2496. else
  2497. {
  2498. result = DatabaseConfigurationResult.RestartNotRequired;
  2499. }
  2500. if ((newsettings.DatabaseType == DatabaseType.Standalone) && (newsettings.ColorScheme != App.DatabaseSettings.ColorScheme))
  2501. {
  2502. App.DatabaseSettings.ColorScheme = newsettings.ColorScheme;
  2503. ApplyColorScheme();
  2504. }
  2505. }
  2506. else
  2507. {
  2508. result = DatabaseConfigurationResult.Cancel;
  2509. }
  2510. return result;
  2511. }
  2512. private void DatabaseSettings_OnClick(object sender, RoutedEventArgs e)
  2513. {
  2514. switch (ShowDatabaseConfiguration())
  2515. {
  2516. case DatabaseConfigurationResult.RestartRequired:
  2517. MessageBox.Show("Please restart the application to apply these changes!");
  2518. break;
  2519. }
  2520. }
  2521. private void CompanyInformation_OnClick(object sender, RoutedEventArgs e)
  2522. {
  2523. var info = new Client<CompanyInformation>().Load().FirstOrDefault();
  2524. if (info == null)
  2525. info = new CompanyInformation();
  2526. new DynamicDataGrid<CompanyInformation>().EditItems(new[] { info });
  2527. }
  2528. private void SecurityDefaultsButton_OnClick(object sender, RoutedEventArgs e)
  2529. {
  2530. var window = new GlobalTokenWindow();
  2531. window.ShowDialog();
  2532. Security.Reset();
  2533. }
  2534. private void SystemLogsButton_OnClick(object sender, RoutedEventArgs e)
  2535. {
  2536. var logfile = CoreUtils.GetLogFile();
  2537. if (File.Exists(logfile))
  2538. {
  2539. var startInfo = new ProcessStartInfo("notepad.exe", logfile);
  2540. startInfo.Verb = "open";
  2541. startInfo.UseShellExecute = true;
  2542. Process.Start(startInfo);
  2543. }
  2544. else
  2545. {
  2546. MessageBox.Show(logfile + " does not exist!");
  2547. }
  2548. }
  2549. private void CheckForUpdates_OnClick(object sender, RoutedEventArgs e)
  2550. {
  2551. if (SupportUtils.CheckForUpdates())
  2552. {
  2553. Close();
  2554. }
  2555. else
  2556. {
  2557. if (MessageWindow.ShowYesNo(
  2558. "You appear to be using the latest version already!\n\nRun the installer anyway?", "Update"))
  2559. {
  2560. if (SupportUtils.DownloadAndRunInstaller())
  2561. {
  2562. Close();
  2563. }
  2564. }
  2565. }
  2566. }
  2567. private void OpenSupportSession_OnClick(object sender, RoutedEventArgs e)
  2568. {
  2569. SupportUtils.OpenSupportSession();
  2570. }
  2571. private void DocumentTypeList_OnClick(object sender, RoutedEventArgs e)
  2572. {
  2573. var list = new MasterList(typeof(DocumentType));
  2574. list.ShowDialog();
  2575. }
  2576. private void EventList_Click(object sender, RoutedEventArgs e)
  2577. {
  2578. var list = new MasterList(typeof(Event));
  2579. list.ShowDialog();
  2580. }
  2581. private void EditDetailsButton_OnClick(object sender, RoutedEventArgs e)
  2582. {
  2583. var employee = new Client<Employee>().Query(
  2584. Filter<Employee>.Where(x => x.UserLink.ID).IsEqualTo(ClientFactory.UserGuid))
  2585. .Rows.FirstOrDefault()?.ToObject<Employee>();
  2586. var item = new MyDetailsConfiguration(employee);
  2587. item.User = ClientFactory.UserGuid;
  2588. var buttons = new DynamicEditorButtons();
  2589. buttons.Add("Change Password", null, item, (sender, item) =>
  2590. {
  2591. var details = (item as MyDetailsConfiguration)!;
  2592. var changePassword = new ChangePassword(details.User);
  2593. if (changePassword.ShowDialog() == true && changePassword.Password != null)
  2594. {
  2595. var newUser = new User();
  2596. newUser.SetID(details.User);
  2597. ChangePassword.ChangeUserPassword(newUser, changePassword.Password);
  2598. new Client<User>().Save(newUser, "Changed Password");
  2599. MessageBox.Show("Password changed!");
  2600. }
  2601. });
  2602. var editor = new DynamicEditorForm(typeof(MyDetailsConfiguration), buttons: buttons);
  2603. if (employee == null)
  2604. {
  2605. editor.OnFormCustomiseEditor += (sender, items, column, editor) =>
  2606. {
  2607. editor.Editable = Editable.Disabled;
  2608. };
  2609. }
  2610. editor.Items = new BaseObject[] { item };
  2611. if (editor.ShowDialog() == true)
  2612. {
  2613. if (employee != null)
  2614. {
  2615. item.SaveTo(employee);
  2616. new Client<Employee>().Save(employee, "Edited by user");
  2617. }
  2618. }
  2619. }
  2620. private void LogoutButton_OnClick(object sender, RoutedEventArgs e)
  2621. {
  2622. Logout();
  2623. }
  2624. private void LoginButton_OnClick(object sender, RoutedEventArgs e)
  2625. {
  2626. if (DoLogin() == ValidationStatus.VALID)
  2627. AfterLogin(null);
  2628. }
  2629. private void ExitButton_OnClick(object sender, RoutedEventArgs e)
  2630. {
  2631. //_closingFromSystemMenu = true;
  2632. Close();
  2633. }
  2634. #endregion
  2635. }